From d1d5972201dbc80d3045b22402b74d4660a8c7cd Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 5 Jun 2025 12:22:15 +0200 Subject: [PATCH 01/45] feat: add value for enum in api we assume that enum has an int value, that is not a case in java. The java enums are objects, which are set constants. Not narrowed to an integer type. To make them work within our ecosystem that includes other technologies, where enums are integer based, I added a int field value and functions to obtain the value for serialization, and create an enum based on the integer. This is part of adding an andorid-jni feature. --- templates/api/api.java.tpl | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/templates/api/api.java.tpl b/templates/api/api.java.tpl index acd1d3e..06ad1b7 100644 --- a/templates/api/api.java.tpl +++ b/templates/api/api.java.tpl @@ -8,10 +8,27 @@ public class {{Camel .Module.Name}} { // enumerations {{- range .Module.Enums }} public enum {{Camel .Name}} { - {{- range .Members }} - @JsonProperty("{{.Value}}") - {{Camel .Name}}, - {{- end }} + {{- range $idx, $m :=.Members }} + {{- if $idx}}, {{ end -}} + {{Camel .Name}}({{.Value}}) + {{- end }}; + + private final int value; + + {{Camel .Name}}(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static {{Camel .Name}} fromValue(int value) { + for ({{Camel .Name}} e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown code: " + code); + } } {{- end }} From 8652907f3724494a68a47067c209bfdc8a5dfc46 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:49:38 +0200 Subject: [PATCH 02/45] chores: change the structure of files it is not a good practice to have a modules names that have dot Usually the dot separates submodules This is part of adding the android-jni feature. --- rules.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules.yaml b/rules.yaml index 0f577f2..2685d84 100644 --- a/rules.yaml +++ b/rules.yaml @@ -3,7 +3,8 @@ features: - name: api scopes: - match: module - prefix: "{{dot .Module.Name}}.api/" + prefix: "{{dot .Module.Name}}/api/" documents: - source: "api/api.java.tpl" target: "{{Camel .Module.Name}}.java" + From 24ff18a3926f44d081da9ca22eb438055fd8eb7f Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:15:29 +0200 Subject: [PATCH 03/45] feat(api): extend interface add the async version of methods. When using with rpc we don't want to block main thread, the rpc anyway assumes the answer will arrive at some point later. add the isReady property, which is also required when using rpc mechanisms. This is part of adding the android-jni feature. --- templates/api/api.java.tpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/api/api.java.tpl b/templates/api/api.java.tpl index 06ad1b7..13c9fea 100644 --- a/templates/api/api.java.tpl +++ b/templates/api/api.java.tpl @@ -68,8 +68,10 @@ public class {{Camel .Module.Name}} { // methods {{- range .Operations }} {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}); + {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}); {{- end }} + bool _isReady(); // signal listeners void addEventListener(I{{Camel .Name }}EventListener listener); void removeEventListener(I{{Camel .Name }}EventListener listener); From 738419d7d938dd6408c9427165e66b37f0fc19e0 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:36:21 +0200 Subject: [PATCH 04/45] fix(api): add and remove event listner the generated names were incorrect, that is fixed now. This is part of adding the android-jni feature. --- templates/api/api.java.tpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/api/api.java.tpl b/templates/api/api.java.tpl index 13c9fea..0a35775 100644 --- a/templates/api/api.java.tpl +++ b/templates/api/api.java.tpl @@ -73,23 +73,23 @@ public class {{Camel .Module.Name}} { bool _isReady(); // signal listeners - void addEventListener(I{{Camel .Name }}EventListener listener); + int addEventListener(I{{Camel .Name }}EventListener listener); void removeEventListener(I{{Camel .Name }}EventListener listener); } public static class Abstract{{Camel .Name}} implements I{{Camel .Name }} { - public Collection events = new HashSet<>(); + public Collection events = new HashSet<>(); - public void addEventListener(IVoidInterfaceEventListener listener) { + public void addEventListener(I{{Camel .Name }}EventListener listener) { listeners.add(listener); } - public void removeEventListener(IVoidInterfaceEventListener listener) { + public void removeEventListener(I{{Camel .Name }}EventListener listener) { listeners.remove(listener); } {{- range .Properties }} @Override public void fire{{Camel .Name}}Changed({{javaType "" .}} oldValue, {{javaType "" .}} newValue) { - for (IVoidInterfaceEventListener listener : events) { + for (I{{Camel .Name }}EventListener listener : events) { listener.on{{Camel .Name}}Changed(oldValue, newValue); } } From a408c3eb65cf62bedde0416bfdce50deba5e81d0 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:41:42 +0200 Subject: [PATCH 05/45] feat(api): property change now has only new value The reason for change is alignment with other techologies implementations This is part of adding the android-jni feature. --- templates/api/api.java.tpl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/templates/api/api.java.tpl b/templates/api/api.java.tpl index 0a35775..765ac41 100644 --- a/templates/api/api.java.tpl +++ b/templates/api/api.java.tpl @@ -51,7 +51,7 @@ public class {{Camel .Module.Name}} { {{- range .Module.Interfaces }} public static interface I{{Camel .Name }}EventListener { {{- range .Properties }} - void on{{Camel .Name}}Changed({{javaType "" .}} oldValue, {{javaType "" .}} newValue); + void on{{Camel .Name}}Changed({{javaType "" .}} newValue); {{- end }} {{- range .Signals }} void on{{Camel .Name}}({{javaParams "" .Params}}); @@ -63,14 +63,16 @@ public class {{Camel .Module.Name}} { {{- range .Properties }} void set{{Camel .Name}}({{javaParam "" .}}); {{javaReturn "" . }} get{{Camel .Name}}(); - void fire{{Camel .Name}}Changed({{javaType "" .}} oldValue, {{javaType "" .}} newValue); + void fire{{Camel .Name}}Changed({{javaType "" .}} newValue); {{ end }} // methods {{- range .Operations }} {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}); {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}); {{- end }} - + {{- range .Signals }} + public void fire{{Camel .Name}}({{javaParams "" .Params}}); + {{- end }} bool _isReady(); // signal listeners int addEventListener(I{{Camel .Name }}EventListener listener); @@ -88,9 +90,9 @@ public class {{Camel .Module.Name}} { } {{- range .Properties }} @Override - public void fire{{Camel .Name}}Changed({{javaType "" .}} oldValue, {{javaType "" .}} newValue) { + public void fire{{Camel .Name}}Changed({{javaType "" .}} newValue) { for (I{{Camel .Name }}EventListener listener : events) { - listener.on{{Camel .Name}}Changed(oldValue, newValue); + listener.on{{Camel .Name}}Changed(newValue); } } {{ end }} From 889a7032a71243b86c6f5319083608891ddb2059 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:47:30 +0200 Subject: [PATCH 06/45] feat(api): split api file the java convention (and ease of use) is that each class or struct has its own file. The api was split to fulfill that practice. This is part of adding the android-jni feature. --- rules.yaml | 23 ++++-- templates/api/abstract.java.tpl | 43 +++++++++++ templates/api/api.java.tpl | 110 --------------------------- templates/api/enum.java.tpl | 30 ++++++++ templates/api/eventlistener.java.tpl | 17 +++++ templates/api/interface.java.tpl | 33 ++++++++ templates/api/struct.java.tpl | 18 +++++ 7 files changed, 159 insertions(+), 115 deletions(-) create mode 100644 templates/api/abstract.java.tpl delete mode 100644 templates/api/api.java.tpl create mode 100644 templates/api/enum.java.tpl create mode 100644 templates/api/eventlistener.java.tpl create mode 100644 templates/api/interface.java.tpl create mode 100644 templates/api/struct.java.tpl diff --git a/rules.yaml b/rules.yaml index 2685d84..8fd9a7f 100644 --- a/rules.yaml +++ b/rules.yaml @@ -2,9 +2,22 @@ languages: ["java"] features: - name: api scopes: - - match: module - prefix: "{{dot .Module.Name}}/api/" + - match: interface + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" documents: - - source: "api/api.java.tpl" - target: "{{Camel .Module.Name}}.java" - + - source: "api/eventlistener.java.tpl" + target: "I{{Camel .Interface.Name }}EventListener.java" + - source: "api/abstract.java.tpl" + target: "Abstract{{Camel .Interface.Name}}.java" + - source: "api/interface.java.tpl" + target: "I{{Camel .Interface.Name}}.java" + - match: struct + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" + documents: + - source: "api/struct.java.tpl" + target: "{{Camel .Struct.Name }}.java" + - match: enum + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" + documents: + - source: "api/enum.java.tpl" + target: "{{Camel .Enum.Name }}.java" diff --git a/templates/api/abstract.java.tpl b/templates/api/abstract.java.tpl new file mode 100644 index 0000000..bf38ca5 --- /dev/null +++ b/templates/api/abstract.java.tpl @@ -0,0 +1,43 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_api; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +//TODO imported/extern modules +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + +import java.util.Collection; +import java.util.HashSet; + +{{- $interfaceName := printf "I%s" (Camel .Interface.Name) }} + public abstract class Abstract{{Camel .Interface.Name}} implements {{$interfaceName}} { + public Collection<{{$interfaceName}}EventListener> listeners = new HashSet<>(); + + public void addEventListener({{$interfaceName}}EventListener listener) { + listeners.add(listener); + } + public void removeEventListener({{$interfaceName}}EventListener listener) { + listeners.remove(listener); + } + {{- range .Interface.Properties }} + @Override + public void fire{{Camel .Name}}Changed({{javaType "" .}} newValue) { + for ({{$interfaceName}}EventListener listener : listeners) { + listener.on{{Camel .Name}}Changed(newValue); + } + } + {{ end }} + {{- range .Interface.Signals }} + @Override + public void fire{{Camel .Name}}({{javaParams "" .Params}}) { + for ({{$interfaceName}}EventListener listener : listeners) { + listener.on{{Camel .Name}}({{ javaVars .Params}}); + } + } + {{ end }} + + } diff --git a/templates/api/api.java.tpl b/templates/api/api.java.tpl deleted file mode 100644 index 765ac41..0000000 --- a/templates/api/api.java.tpl +++ /dev/null @@ -1,110 +0,0 @@ -package {{dot .Module.Name}}.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class {{Camel .Module.Name}} { - - // enumerations - {{- range .Module.Enums }} - public enum {{Camel .Name}} { - {{- range $idx, $m :=.Members }} - {{- if $idx}}, {{ end -}} - {{Camel .Name}}({{.Value}}) - {{- end }}; - - private final int value; - - {{Camel .Name}}(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - - public static {{Camel .Name}} fromValue(int value) { - for ({{Camel .Name}} e : values()) { - if (e.value == value) return e; - } - throw new IllegalArgumentException("Unknown code: " + code); - } - } - {{- end }} - - // data structures - {{- range .Module.Structs }} - public static class {{Camel .Name}} { - public {{Camel .Name}}({{javaParams "" .Fields}}) { - {{- range .Fields }} - this.{{camel .Name}} = {{camel .Name}}; - {{- end }} - } - {{- range .Fields }} - @JsonProperty("{{snake .Name}}") - public {{javaType "" .}} {{camel .Name}}; - {{- end }} - } - {{- end }} - - // interfaces - {{- range .Module.Interfaces }} - public static interface I{{Camel .Name }}EventListener { - {{- range .Properties }} - void on{{Camel .Name}}Changed({{javaType "" .}} newValue); - {{- end }} - {{- range .Signals }} - void on{{Camel .Name}}({{javaParams "" .Params}}); - {{- end }} - } - - public static interface I{{Camel .Name }} { - // properties - {{- range .Properties }} - void set{{Camel .Name}}({{javaParam "" .}}); - {{javaReturn "" . }} get{{Camel .Name}}(); - void fire{{Camel .Name}}Changed({{javaType "" .}} newValue); - {{ end }} - // methods - {{- range .Operations }} - {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}); - {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}); - {{- end }} - {{- range .Signals }} - public void fire{{Camel .Name}}({{javaParams "" .Params}}); - {{- end }} - bool _isReady(); - // signal listeners - int addEventListener(I{{Camel .Name }}EventListener listener); - void removeEventListener(I{{Camel .Name }}EventListener listener); - } - - public static class Abstract{{Camel .Name}} implements I{{Camel .Name }} { - public Collection events = new HashSet<>(); - - public void addEventListener(I{{Camel .Name }}EventListener listener) { - listeners.add(listener); - } - public void removeEventListener(I{{Camel .Name }}EventListener listener) { - listeners.remove(listener); - } - {{- range .Properties }} - @Override - public void fire{{Camel .Name}}Changed({{javaType "" .}} newValue) { - for (I{{Camel .Name }}EventListener listener : events) { - listener.on{{Camel .Name}}Changed(newValue); - } - } - {{ end }} - {{- range .Signals }} - @Override - public void fire{{Camel .Name}}({{javaParams "" .Params}}) { - for (I{{Camel .Name }}EventListener listener : events) { - listener.on{{Camel .Name}}({{ javaVars .Params}}); - } - } - {{ end }} - - } - {{- end }} -} \ No newline at end of file diff --git a/templates/api/enum.java.tpl b/templates/api/enum.java.tpl new file mode 100644 index 0000000..0957f17 --- /dev/null +++ b/templates/api/enum.java.tpl @@ -0,0 +1,30 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + + public enum {{Camel .Enum.Name}} { + {{- range $idx, $m :=.Enum.Members }} + {{- if $idx}}, {{ end -}} + @JsonProperty("{{.Value}}") + {{Camel .Name}}({{.Value}}) + {{- end }}; + + private final int value; + + {{Camel .Enum.Name}}(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static {{Camel .Enum.Name}} fromValue(int value) { + for ({{Camel .Enum.Name}} e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown code: " + code); + } + } + +} \ No newline at end of file diff --git a/templates/api/eventlistener.java.tpl b/templates/api/eventlistener.java.tpl new file mode 100644 index 0000000..f51741e --- /dev/null +++ b/templates/api/eventlistener.java.tpl @@ -0,0 +1,17 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_api; + +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + + public interface I{{Camel .Interface.Name }}EventListener { + {{- range .Interface.Properties }} + void on{{Camel .Name}}Changed({{javaType "" .}} newValue); + {{- end }} + {{- range .Interface.Signals }} + void on{{Camel .Name}}({{javaParams "" .Params}}); + {{- end }} + } diff --git a/templates/api/interface.java.tpl b/templates/api/interface.java.tpl new file mode 100644 index 0000000..6ba6952 --- /dev/null +++ b/templates/api/interface.java.tpl @@ -0,0 +1,33 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_api; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + +import java.util.concurrent.CompletableFuture; + + + public interface I{{Camel .Interface.Name }} { + // properties + {{- range .Interface.Properties }} + void set{{Camel .Name}}({{javaParam "" .}}); + {{javaReturn "" . }} get{{Camel .Name}}(); + void fire{{Camel .Name}}Changed({{javaType "" .}} newValue); + {{ end }} + // methods + {{- range .Interface.Operations }} + {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}); + {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}); + {{- end }} + {{- range .Interface.Signals }} + public void fire{{Camel .Name}}({{javaParams "" .Params}}); + {{- end }} + boolean _isReady(); + // signal listeners + void addEventListener(I{{Camel .Interface.Name }}EventListener listener); + void removeEventListener(I{{Camel .Interface.Name }}EventListener listener); + } diff --git a/templates/api/struct.java.tpl b/templates/api/struct.java.tpl new file mode 100644 index 0000000..f0494b8 --- /dev/null +++ b/templates/api/struct.java.tpl @@ -0,0 +1,18 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +//TODO imports - may need some struct from this or imported module + + public class {{Camel .Struct.Name}} { + + public {{Camel .Struct.Name}}({{javaParams "" .Struct.Fields}}) { + {{- range .Struct.Fields }} + this.{{camel .Name}} = {{camel .Name}}; + {{- end }} + } + {{- range .Struct.Fields }} + @JsonProperty("{{snake .Name}}") + public {{javaType "" .}} {{camel .Name}}; + {{- end }} + } From f765c35acf659916534a704fe2eac7408bac6e20 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:09:01 +0200 Subject: [PATCH 07/45] feat(api): add gradle project files adds necessary gradle files to allow the project to be build with e.g. android studio. The gradle was chosen because of compatibility with unreal build system, as the main purpose of all the changes in the series of commits is to allow easy data change between unreal and android. Most of the modules will be used with unreal to achieve that. This is part of adding the android-jni feature. --- rules.yaml | 15 +++++++++++++++ templates/api/additions.gradle.tpl | 7 +++++++ templates/api/build.gradle.tpl | 12 ++++++++++++ templates/gradle.properties | 21 +++++++++++++++++++++ templates/libs.versions.toml | 27 +++++++++++++++++++++++++++ templates/settings.gradle.tpl | 24 ++++++++++++++++++++++++ 6 files changed, 106 insertions(+) create mode 100644 templates/api/additions.gradle.tpl create mode 100644 templates/api/build.gradle.tpl create mode 100644 templates/gradle.properties create mode 100644 templates/libs.versions.toml create mode 100644 templates/settings.gradle.tpl diff --git a/rules.yaml b/rules.yaml index 8fd9a7f..62c64db 100644 --- a/rules.yaml +++ b/rules.yaml @@ -2,6 +2,21 @@ languages: ["java"] features: - name: api scopes: + - match: module + prefix: "{{dot .Module.Name}}/" + documents: + - source: "libs.versions.toml" + target: "gradle/libs.versions.toml" + raw: true + - source: "settings.gradle.tpl" + target: "settings.gradle" + - source: "gradle.properties" + target: "gradle.properties" + raw: true + - source: "api/build.gradle.tpl" + target: "{{dot .Module.Name}}_api/build.gradle" + - source: "api/additions.gradle.tpl" + target: "{{dot .Module.Name}}_api/additions.gradle" - match: interface prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" documents: diff --git a/templates/api/additions.gradle.tpl b/templates/api/additions.gradle.tpl new file mode 100644 index 0000000..c36c0da --- /dev/null +++ b/templates/api/additions.gradle.tpl @@ -0,0 +1,7 @@ +android { + namespace '{{camel .Module.Name}}.{{dot .Module.Name}}_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/templates/api/build.gradle.tpl b/templates/api/build.gradle.tpl new file mode 100644 index 0000000..684828d --- /dev/null +++ b/templates/api/build.gradle.tpl @@ -0,0 +1,12 @@ +plugins { + id 'java-library' +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/templates/gradle.properties b/templates/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/templates/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/templates/libs.versions.toml b/templates/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/templates/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl new file mode 100644 index 0000000..686797b --- /dev/null +++ b/templates/settings.gradle.tpl @@ -0,0 +1,24 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "{{camel .Module.Name}}" +include ':{{camel .Module.Name}}_api' + From c62050acc3e1920e95ac215e67edd373b6f2f5ff Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:26:41 +0200 Subject: [PATCH 08/45] feat(android): add skeleton for service adapter an android service is created for each interface. It allows setting a backend that fullfills the interface. The intention is to be albe to provide java-jni impl e.g. by unreal (but it can bu also pure java implementation). The backend is created by factory, so it is in same thread as the service. Note that the services are created in applications thread, not in a separate ones, so having many interfaces and thus many services does not cause the many threads overhead. The messenger part is not yet there. It will be added with following tasks. The service must be started implicitely with context.startService(theService) function. This is part of adding the android-jni feature. --- rules.yaml | 9 + .../android/service/iservicefactory.java.tpl | 7 + .../android/service/serviceadapter.java.tpl | 206 ++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 templates/android/service/iservicefactory.java.tpl create mode 100644 templates/android/service/serviceadapter.java.tpl diff --git a/rules.yaml b/rules.yaml index 62c64db..c630c46 100644 --- a/rules.yaml +++ b/rules.yaml @@ -36,3 +36,12 @@ features: documents: - source: "api/enum.java.tpl" target: "{{Camel .Enum.Name }}.java" + - name: android + scopes: + - match: interface + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" + documents: + - source: "android/service/iservicefactory.java.tpl" + target: "I{{Camel .Interface.Name}}ServiceFactory.java" + - source: "android/service/serviceadapter.java.tpl" + target: "{{Camel .Interface.Name}}ServiceAdapter.java" diff --git a/templates/android/service/iservicefactory.java.tpl b/templates/android/service/iservicefactory.java.tpl new file mode 100644 index 0000000..7b5c5fc --- /dev/null +++ b/templates/android/service/iservicefactory.java.tpl @@ -0,0 +1,7 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; + + +public interface I{{Camel .Interface.Name}}ServiceFactory { + public I{{Camel .Interface.Name }} getServiceInstance(); +} diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl new file mode 100644 index 0000000..fad49b7 --- /dev/null +++ b/templates/android/service/serviceadapter.java.tpl @@ -0,0 +1,206 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class {{Camel .Interface.Name }}ServiceAdapter extends Service +{ + private static final String TAG = "{{Camel .Interface.Name }}ServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static I{{Camel .Interface.Name}} mBackendService; + private static I{{Camel .Interface.Name}}ServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public {{Camel .Interface.Name }}ServiceAdapter() + { + } + + public static I{{Camel .Interface.Name}} setService(I{{Camel .Interface.Name}}ServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate({{Camel .Interface.Name }}Service) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: {{Camel .Interface.Name }}Service::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy({{Camel .Interface.Name }}Service) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy({{Camel .Interface.Name }}Service) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements I{{Camel .Interface.Name }}EventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mActivityClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToActivityClients(Message msg) + { + for (Map.Entry client : mActivityClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + //TODO switch on messages + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mActivityClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mActivityClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + {{- range .Interface.Properties }} + @Override + public void on{{Camel .Name}}Changed({{javaType "" .}} newValue){ + Log.i(TAG, "New value for {{Camel .Name}} from backend" + newValue); + } + {{- end }} + {{- range .Interface.Signals }} + @Override + public void on{{Camel .Name}}({{javaParams "" .Params}}){ + Log.i(TAG, "New singal for {{Camel .Name}} = " + {{javaVars .Params}}); + } + {{- end }} + } +} From 219851325b2d1564fd5937f8e180e2c7c7791fd1 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:15:43 +0200 Subject: [PATCH 09/45] feat(android): add android service project files add gradle files with which it can be build with android studio or with unreal. This is part of adding the android-jni feature. --- rules.yaml | 9 +++++++ .../android/service/AndroidManifest.xml.tpl | 23 ++++++++++++++++ .../android/service/additions.gradle.tpl | 18 +++++++++++++ templates/android/service/build.gradle.tpl | 27 +++++++++++++++++++ templates/settings.gradle.tpl | 1 + 5 files changed, 78 insertions(+) create mode 100644 templates/android/service/AndroidManifest.xml.tpl create mode 100644 templates/android/service/additions.gradle.tpl create mode 100644 templates/android/service/build.gradle.tpl diff --git a/rules.yaml b/rules.yaml index c630c46..a35cc79 100644 --- a/rules.yaml +++ b/rules.yaml @@ -38,6 +38,15 @@ features: target: "{{Camel .Enum.Name }}.java" - name: android scopes: + - match: module + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" + documents: + - source: "android/service/build.gradle.tpl" + target: "build.gradle" + - source: "android/service/additions.gradle.tpl" + target: "additions.gradle" + - source: "android/service/AndroidManifest.xml.tpl" + target: "src/main/AndroidManifest.xml" - match: interface prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" documents: diff --git a/templates/android/service/AndroidManifest.xml.tpl b/templates/android/service/AndroidManifest.xml.tpl new file mode 100644 index 0000000..e5d93c4 --- /dev/null +++ b/templates/android/service/AndroidManifest.xml.tpl @@ -0,0 +1,23 @@ + + + + + + + + + + + + {{- range .Module.Interfaces -}} + + + {{- end }} + + \ No newline at end of file diff --git a/templates/android/service/additions.gradle.tpl b/templates/android/service/additions.gradle.tpl new file mode 100644 index 0000000..f6d0bb0 --- /dev/null +++ b/templates/android/service/additions.gradle.tpl @@ -0,0 +1,18 @@ +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/android/service/build.gradle.tpl b/templates/android/service/build.gradle.tpl new file mode 100644 index 0000000..09aae0a --- /dev/null +++ b/templates/android/service/build.gradle.tpl @@ -0,0 +1,27 @@ +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_service' + compileSdk 35 + + defaultConfig { + minSdk 34 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index 686797b..1cef6c5 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -20,5 +20,6 @@ dependencyResolutionManagement { } rootProject.name = "{{camel .Module.Name}}" +include ':{{camel .Module.Name}}_android_service' include ':{{camel .Module.Name}}_api' From 50311c02d8fb6726932f883ea753a949468f26c9 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:09:04 +0200 Subject: [PATCH 10/45] feat(stub): add stub feature adds an empty implementation of generated interface. It fully supports handling properties (with the property change singal emission). It generates empty methods, to be filled with bussiness logic. It may serve as an example or base implementation. It allows easy implementing android service side when provideded as a backend, hence all the helper file, facory, and service starter which implicitely start the service. This is part of adding the android-jni feature. --- rules.yaml | 24 +++++ .../android/service/additions.gradle.tpl | 1 + templates/android/service/build.gradle.tpl | 1 + .../service/implservicefactory.java.tpl | 81 +++++++++++++++ .../service/implservicestarter.java.tpl | 42 ++++++++ templates/api/struct.java.tpl | 2 + templates/settings.gradle.tpl | 8 +- templates/stub/additions.gradle.tpl | 18 ++++ templates/stub/build.gradle.tpl | 27 +++++ templates/stub/implservice.java.tpl | 99 +++++++++++++++++++ 10 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 templates/android/service/implservicefactory.java.tpl create mode 100644 templates/android/service/implservicestarter.java.tpl create mode 100644 templates/stub/additions.gradle.tpl create mode 100644 templates/stub/build.gradle.tpl create mode 100644 templates/stub/implservice.java.tpl diff --git a/rules.yaml b/rules.yaml index a35cc79..d53b9fa 100644 --- a/rules.yaml +++ b/rules.yaml @@ -36,7 +36,26 @@ features: documents: - source: "api/enum.java.tpl" target: "{{Camel .Enum.Name }}.java" + - name: stubs + requires: + - api + scopes: + - match: module + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_impl/" + documents: + - source: "stub/build.gradle.tpl" + target: "build.gradle" + - source: "stub/additions.gradle.tpl" + target: "additions.gradle" + - match: interface + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_impl/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_impl/" + documents: + - source: "stub/implservice.java.tpl" + target: "{{Camel .Interface.Name}}Service.java" - name: android + requires: + - api + - stubs scopes: - match: module prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" @@ -54,3 +73,8 @@ features: target: "I{{Camel .Interface.Name}}ServiceFactory.java" - source: "android/service/serviceadapter.java.tpl" target: "{{Camel .Interface.Name}}ServiceAdapter.java" + - source: "android/service/implservicefactory.java.tpl" + target: "{{Camel .Interface.Name}}ServiceFactory.java" + - source: "android/service/implservicestarter.java.tpl" + target: "{{Camel .Interface.Name }}ServiceStarter.java" + diff --git a/templates/android/service/additions.gradle.tpl b/templates/android/service/additions.gradle.tpl index f6d0bb0..09575e2 100644 --- a/templates/android/service/additions.gradle.tpl +++ b/templates/android/service/additions.gradle.tpl @@ -11,6 +11,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') + implementation project(':{{camel .Module.Name}}_impl') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/service/build.gradle.tpl b/templates/android/service/build.gradle.tpl index 09aae0a..1ac4487 100644 --- a/templates/android/service/build.gradle.tpl +++ b/templates/android/service/build.gradle.tpl @@ -20,6 +20,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') + implementation project(':{{camel .Module.Name}}_impl') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/service/implservicefactory.java.tpl b/templates/android/service/implservicefactory.java.tpl new file mode 100644 index 0000000..27c9a49 --- /dev/null +++ b/templates/android/service/implservicefactory.java.tpl @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name }}ServiceFactory; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_impl.{{Camel .Interface.Name}}Service; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton {{Camel .Interface.Name}}ServiceFactory thread for the system. This is a thread for + * {{Camel .Interface.Name}}ServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class {{Camel .Interface.Name}}ServiceFactory extends HandlerThread implements I{{Camel .Interface.Name }}ServiceFactory +{ + private {{Camel .Interface.Name}}Service m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static {{Camel .Interface.Name}}ServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: {{Camel .Interface.Name}}ServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized Abstract{{Camel .Interface.Name }} getServiceInstance() + { + if (m_Service == null) + { + m_Service = new {{Camel .Interface.Name}}Service(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new {{Camel .Interface.Name}}Service"); + } + + return m_Service; + } + + private static class Singleton + { + private static final {{Camel .Interface.Name}}ServiceFactory INSTANCE = createInstance(); + } + + private {{Camel .Interface.Name}}ServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static {{Camel .Interface.Name}}ServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + {{Camel .Interface.Name}}ServiceFactory t = new {{Camel .Interface.Name}}ServiceFactory(); + t.start(); + return t; + } +} diff --git a/templates/android/service/implservicestarter.java.tpl b/templates/android/service/implservicestarter.java.tpl new file mode 100644 index 0000000..54106b3 --- /dev/null +++ b/templates/android/service/implservicestarter.java.tpl @@ -0,0 +1,42 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name}}ServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package {{camel .Module.Name}}.{{camel .Module.Name}}_impl; . +public class {{Camel .Interface.Name }}ServiceStarter { + + static Intent androidService = null; + private static final String TAG = "{{Camel .Interface.Name }}Starter"; + + + + public static I{{Camel .Interface.Name }} start(Context context) { + stop(context); + androidService = new Intent(context, {{Camel .Interface.Name }}ServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + {{Camel .Interface.Name}}ServiceFactory factory = {{Camel .Interface.Name}}ServiceFactory.get(); + Log.w(TAG, "starter: factory set for {{Camel .Interface.Name}}ServiceFactory"); + return {{Camel .Interface.Name }}ServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/templates/api/struct.java.tpl b/templates/api/struct.java.tpl index f0494b8..65b9256 100644 --- a/templates/api/struct.java.tpl +++ b/templates/api/struct.java.tpl @@ -11,6 +11,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; this.{{camel .Name}} = {{camel .Name}}; {{- end }} } + + public {{Camel .Struct.Name}}() {} {{- range .Struct.Fields }} @JsonProperty("{{snake .Name}}") public {{javaType "" .}} {{camel .Name}}; diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index 1cef6c5..aa5818b 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -20,6 +20,12 @@ dependencyResolutionManagement { } rootProject.name = "{{camel .Module.Name}}" +{{- if .Features.android }} include ':{{camel .Module.Name}}_android_service' +{{- end -}} +{{- if .Features.stubs }} +include ':{{camel .Module.Name}}_impl' +{{- end -}} +{{- if .Features.api }} include ':{{camel .Module.Name}}_api' - +{{- end -}} diff --git a/templates/stub/additions.gradle.tpl b/templates/stub/additions.gradle.tpl new file mode 100644 index 0000000..5dea7e1 --- /dev/null +++ b/templates/stub/additions.gradle.tpl @@ -0,0 +1,18 @@ +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/stub/build.gradle.tpl b/templates/stub/build.gradle.tpl new file mode 100644 index 0000000..201eae4 --- /dev/null +++ b/templates/stub/build.gradle.tpl @@ -0,0 +1,27 @@ +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_impl' + compileSdk 35 + + defaultConfig { + minSdk 34 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl new file mode 100644 index 0000000..c29002f --- /dev/null +++ b/templates/stub/implservice.java.tpl @@ -0,0 +1,99 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_impl; + +import android.os.Messenger; +import android.util.Log; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface.Name}} { + + + private final static String TAG = "{{Camel .Interface.Name}}Service"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + {{- range .Interface.Properties }} + private {{javaReturn "" .}} m_{{javaVar .}} = {{ javaDefault "" . }}; + {{- end}} + +{{- range .Interface.Properties }} + @Override + public void set{{Camel .Name}}({{javaParam "" .}}) + { + Log.i(TAG, "request set{{Camel .Name}} callede "); + if (m_{{javaVar .}} != {{javaVar .}}) + { + m_{{javaVar .}} = {{javaVar .}}; + on{{Camel .Name}}Changed(m_{{javaVar .}}); + } + + } + + @Override + public {{javaReturn "" . }} get{{Camel .Name}}() + { + Log.i(TAG, "request get{{Camel .Name}} called,"); + return m_{{javaVar .}}; + } + + {{ end }} + // methods + {{- range .Interface.Operations }} + + @Override + public {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}) { + Log.w(TAG, "request method {{camel .Name}} called, returnig default"); + return {{ if not .Return.IsVoid }}{{ javaDefault "" .Return }}{{end }}; + } + + @Override + public {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}) { + return CompletableFuture.{{- if .Return.IsVoid }}runAsync{{else}}supplyAsync{{end}}( + () -> { {{- if not .Return.IsVoid }}return{{end}} {{camel .Name}}({{javaVars .Params }}); }, + executor); + } + + {{- end }} + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + + {{- range .Interface.Properties }} + private void on{{Camel .Name}}Changed({{javaType "" .}} newValue) + { + Log.i(TAG, "on{{Camel .Name}}Changed, will pass notification to all listeners"); + fire{{Camel .Name}}Changed(newValue); + } + {{- end}} + {{- range .Interface.Signals }} + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + Log.i(TAG, "on{{Camel .Name}}, will pass notification to all listeners"); + fire{{Camel .Name}}({{javaVars .Params}}); + } + {{- end }} + +} \ No newline at end of file From 324e4d8470b19f2278b447ffe2be54764c1aea96 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:23:45 +0200 Subject: [PATCH 11/45] feat(android): add messages and basic parcelable Adds all the "topics" of messages (an enum). Introduces a parcelable type that wraps generated structs. This works only for structs with simple types. The parcelable will be extended with handling complex types in later tasks. This is part of adding the android-jni feature. --- rules.yaml | 18 ++++- .../android/messenger/additions.gradle.tpl | 18 +++++ templates/android/messenger/build.gradle.tpl | 27 +++++++ templates/android/messenger/messages.java.tpl | 48 +++++++++++++ .../messenger/structparcelable.java.tpl | 70 +++++++++++++++++++ templates/settings.gradle.tpl | 1 + 6 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 templates/android/messenger/additions.gradle.tpl create mode 100644 templates/android/messenger/build.gradle.tpl create mode 100644 templates/android/messenger/messages.java.tpl create mode 100644 templates/android/messenger/structparcelable.java.tpl diff --git a/rules.yaml b/rules.yaml index d53b9fa..f8f05a3 100644 --- a/rules.yaml +++ b/rules.yaml @@ -77,4 +77,20 @@ features: target: "{{Camel .Interface.Name}}ServiceFactory.java" - source: "android/service/implservicestarter.java.tpl" target: "{{Camel .Interface.Name }}ServiceStarter.java" - + - match: module + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/" + documents: + - source: "android/messenger/build.gradle.tpl" + target: "build.gradle" + - source: "android/messenger/additions.gradle.tpl" + target: "additions.gradle" + - match: interface + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/" + documents: + - source: "android/messenger/messages.java.tpl" + target: "{{Camel .Interface.Name}}MessageType.java" + - match: struct + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/" + documents: + - source: "android/messenger/structparcelable.java.tpl" + target: "{{Camel .Struct.Name }}Parcelable.java" diff --git a/templates/android/messenger/additions.gradle.tpl b/templates/android/messenger/additions.gradle.tpl new file mode 100644 index 0000000..252c9a8 --- /dev/null +++ b/templates/android/messenger/additions.gradle.tpl @@ -0,0 +1,18 @@ +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/android/messenger/build.gradle.tpl b/templates/android/messenger/build.gradle.tpl new file mode 100644 index 0000000..a81cbfc --- /dev/null +++ b/templates/android/messenger/build.gradle.tpl @@ -0,0 +1,27 @@ +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 34 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/android/messenger/messages.java.tpl b/templates/android/messenger/messages.java.tpl new file mode 100644 index 0000000..50d8fa0 --- /dev/null +++ b/templates/android/messenger/messages.java.tpl @@ -0,0 +1,48 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; + +{{ define "inc" }}{{ len (printf "%*s " . "") }}{{ end -}} + +public enum {{Camel .Interface.Name}}MessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + {{- $msgNum := 1}} +{{- range .Interface.Properties }} + {{- $msgNum = len (printf "%*s " $msgNum "" ) }} + PROP_{{Camel .Name}}({{$msgNum}}), + {{- $msgNum = len (printf "%*s " $msgNum "" ) }} + SET_{{Camel .Name}}({{$msgNum}}), +{{- end }} +{{- range .Interface.Signals }} + {{- $msgNum = len (printf "%*s " $msgNum "" ) }} + SIG_{{Camel .Name}}({{$msgNum}}), +{{- end }} +{{- range .Interface.Operations }} + {{- $msgNum = len (printf "%*s " $msgNum "" ) }} + RPC_{{Camel .Name}}Req({{$msgNum}}), + {{- $msgNum = len (printf "%*s " $msgNum "" ) }} + RPC_{{Camel .Name}}Resp({{$msgNum}}), +{{- end }} + {{Camel .Interface.Name}}MessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + {{Camel .Interface.Name}}MessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static {{Camel .Interface.Name}}MessageType fromInteger(int value) + { + for ({{Camel .Interface.Name}}MessageType event : {{Camel .Interface.Name}}MessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return {{Camel .Interface.Name}}MessageType_UNKNOWN; + } +} diff --git a/templates/android/messenger/structparcelable.java.tpl b/templates/android/messenger/structparcelable.java.tpl new file mode 100644 index 0000000..a47cebd --- /dev/null +++ b/templates/android/messenger/structparcelable.java.tpl @@ -0,0 +1,70 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Struct.Name}}; +import android.os.Parcel; +import android.os.Parcelable; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +//TODO imports - may need some struct from this or imported module + + public class {{Camel .Struct.Name}}Parcelable implements Parcelable { + + public {{Camel .Struct.Name}} data; + + public {{Camel .Struct.Name}}Parcelable({{Camel .Struct.Name}} data) { + this.data = data; + } + + public {{Camel .Struct.Name}} get{{Camel .Struct.Name}}() + { + return data; + } + + protected {{Camel .Struct.Name}}Parcelable(Parcel in) { + +{{- range .Struct.Fields }} +{{- if .IsPrimitive }} + {{javaType "" .}} l_{{camel .Name}} = in.read{{ ( Camel (javaType "" .) ) }}(); +{{- end }} +{{- end }} + // TODO arrays in general + // TODO add enums(Arrays) = MyEnumWrapper singleEnumWrapper = in.readParcelable(MyEnumWrapper.class.getClassLoader()); MyEnum singleEnum = singleEnumWrapper != null ? singleEnumWrapper.value : null; + // TODO read other structs same as enums - they all should be parcelable + + this.data = new {{Camel .Struct.Name}}( + {{- range $idx, $m :=.Struct.Fields }}{{- if $idx}}, {{ end -}}l_{{camel .Name}}{{- end }}); + } + + public static final Creator<{{Camel .Struct.Name}}Parcelable> CREATOR = new Creator<{{Camel .Struct.Name}}Parcelable>() { + @Override + public {{Camel .Struct.Name}}Parcelable createFromParcel(Parcel in) { + return new {{Camel .Struct.Name}}Parcelable(in); + } + + @Override + public {{Camel .Struct.Name}}Parcelable[] newArray(int size) { + return new {{Camel .Struct.Name}}Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + {{- range .Struct.Fields }} + {{- if .IsPrimitive }} + dest.write{{ ( Camel (javaType "" .) ) }}(data.{{camel .Name}}); + {{- end }} + {{- end }} + // TODO arrays in general + // TODO add enums + // TODO write other structs same as enums - they all should be parcelable + + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index aa5818b..936719e 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -22,6 +22,7 @@ dependencyResolutionManagement { rootProject.name = "{{camel .Module.Name}}" {{- if .Features.android }} include ':{{camel .Module.Name}}_android_service' +include ':{{camel .Module.Name}}_android_messenger' {{- end -}} {{- if .Features.stubs }} include ':{{camel .Module.Name}}_impl' From 22b18d634830f4fa6c62507160682699f453dd2d Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:13:22 +0200 Subject: [PATCH 12/45] feat(android): add messenger to service Adds handing then messages: calls proper methods on backend when serving incoming messages and sends messages when notified by backend about changes that must be forwarded to clients. Added initial tests. This is part of adding the android-jni feature. --- rules.yaml | 5 + templates/android/messenger/build.gradle.tpl | 2 +- .../messenger/structparcelable.java.tpl | 1 - .../android/service/additions.gradle.tpl | 1 + templates/android/service/build.gradle.tpl | 7 +- .../android/service/serviceadapter.java.tpl | 125 ++++++++++- .../service/serviceadaptertest.java.tpl | 212 ++++++++++++++++++ templates/stub/build.gradle.tpl | 2 +- 8 files changed, 348 insertions(+), 7 deletions(-) create mode 100644 templates/android/service/serviceadaptertest.java.tpl diff --git a/rules.yaml b/rules.yaml index f8f05a3..d5754b6 100644 --- a/rules.yaml +++ b/rules.yaml @@ -77,6 +77,11 @@ features: target: "{{Camel .Interface.Name}}ServiceFactory.java" - source: "android/service/implservicestarter.java.tpl" target: "{{Camel .Interface.Name }}ServiceStarter.java" + - match: interface + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/src/test/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" + documents: + - source: "android/service/serviceadaptertest.java.tpl" + target: "{{Camel .Interface.Name}}ServiceAdapterTest.java" - match: module prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/" documents: diff --git a/templates/android/messenger/build.gradle.tpl b/templates/android/messenger/build.gradle.tpl index a81cbfc..c25b3f2 100644 --- a/templates/android/messenger/build.gradle.tpl +++ b/templates/android/messenger/build.gradle.tpl @@ -7,7 +7,7 @@ android { compileSdk 35 defaultConfig { - minSdk 34 + minSdk 33 targetSdk 34 versionCode 1 versionName "1.0" diff --git a/templates/android/messenger/structparcelable.java.tpl b/templates/android/messenger/structparcelable.java.tpl index a47cebd..67fb916 100644 --- a/templates/android/messenger/structparcelable.java.tpl +++ b/templates/android/messenger/structparcelable.java.tpl @@ -5,7 +5,6 @@ import android.os.Parcel; import android.os.Parcelable; -import com.fasterxml.jackson.annotation.JsonProperty; //TODO imports - may need some struct from this or imported module diff --git a/templates/android/service/additions.gradle.tpl b/templates/android/service/additions.gradle.tpl index 09575e2..d5aae2f 100644 --- a/templates/android/service/additions.gradle.tpl +++ b/templates/android/service/additions.gradle.tpl @@ -12,6 +12,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') implementation project(':{{camel .Module.Name}}_impl') + implementation project(':{{camel .Module.Name}}_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/service/build.gradle.tpl b/templates/android/service/build.gradle.tpl index 1ac4487..cb57413 100644 --- a/templates/android/service/build.gradle.tpl +++ b/templates/android/service/build.gradle.tpl @@ -7,7 +7,7 @@ android { compileSdk 35 defaultConfig { - minSdk 34 + minSdk 33 targetSdk 34 versionCode 1 versionName "1.0" @@ -15,12 +15,17 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + testOptions { + unitTests.includeAndroidResources = true + } + } dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') implementation project(':{{camel .Module.Name}}_impl') + implementation project(':{{camel .Module.Name}}_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index fad49b7..54e4230 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -17,6 +17,7 @@ import android.util.Log; {{- range .Module.Structs }} import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; @@ -26,6 +27,7 @@ import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }} import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -162,10 +164,102 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service } @Override + {{- $InterfaceName := Camel .Interface.Name}} public void handleMessage(Message msg) - { + { Log.i(TAG, "Handle msg " + msg); - //TODO switch on messages + if (mBackendService == null || !mBackendService._isReady()) + { + if ({{Camel .Interface.Name}}MessageType.fromInteger(msg.what) != {{Camel .Interface.Name}}MessageType.REGISTER_CLIENT + && {{Camel .Interface.Name}}MessageType.fromInteger(msg.what) != {{Camel .Interface.Name}}MessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: {{Camel .Interface.Name}}MessageType" + {{Camel .Interface.Name}}MessageType.fromInteger(msg.what) ); + return; + } + } + switch ({{Camel .Interface.Name}}MessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + //TODO ENUMS AND ARRAYS (for array just change the func to getXArray) + {{- range .Interface.Properties }} + case PROP_{{Camel .Name}}: + { + Bundle data = msg.getData(); + {{- if .IsPrimitive }} + {{javaReturn "" .}} newValue = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" .}} newValue = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + + mBackendService.set{{Camel .Name}}(newValue); + break; + } + {{- end }} + {{- range .Interface.Operations }} + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_{{Camel .Name}}Req: { + + Bundle data = msg.getData(); + {{- range .Params }} + {{- if not .IsPrimitive }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} + int callId = data.getInt("callId"); + + {{- range .Params }} + {{- if .IsPrimitive }} + {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else }} + {{javaReturn "" .}}Parcelable {{javaVar .}}Parcelable = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class); + {{javaReturn "" .}} {{javaVar .}} = {{javaVar .}}Parcelable.get{{Camel (javaReturn "" .)}}(); + {{- end }} + {{- end }} + + {{- if .Return.IsPrimitive }} + {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} result = {{- end }}mBackendService.{{camel .Name}}({{javaVars .Params}}); + {{- else }} + {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} result = {{- end }} mBackendService.{{camel .Name}}({{javaVars .Params}}); + {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} + {{javaReturn "" .Return}}Parcelable result = new {{javaReturn "" .Return}}Parcelable(dataResult); + {{- end }} + {{- end }} + + Message respMsg = new Message(); + respMsg.what = {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + {{- if not .Return.IsVoid }} + {{- if (.Return.IsPrimitive) }} + resp_data.put{{ ( Camel (javaType "" .Return) ) }}("result", result); + {{- else }} + resp_data.putParcelable("result", result); + {{- end }} + {{- end }} + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + {{- end }} + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } } @Override @@ -194,13 +288,38 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service @Override public void on{{Camel .Name}}Changed({{javaType "" .}} newValue){ Log.i(TAG, "New value for {{Camel .Name}} from backend" + newValue); + + Message msg = new Message(); + msg.what = {{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue(); + Bundle data = new Bundle(); + {{- if .IsPrimitive }} + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); + {{- end }} + msg.setData(data); + sendMessageToActivityClients(msg); } {{- end }} {{- range .Interface.Signals }} @Override public void on{{Camel .Name}}({{javaParams "" .Params}}){ - Log.i(TAG, "New singal for {{Camel .Name}} = " + {{javaVars .Params}}); + Log.i(TAG, "New singal for {{Camel .Name}} = " + {{- range .Params -}} + " " + {{javaVar .}}{{ end}}); + Message msg = new Message(); + msg.what = {{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue(); + Bundle data = new Bundle(); + {{- range .Params }} + {{- if .IsPrimitive }} + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{ javaVar .}}); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- end }} + {{- end }} + msg.setData(data); + sendMessageToActivityClients(msg); } {{- end }} + } } diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl new file mode 100644 index 0000000..b21ca11 --- /dev/null +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -0,0 +1,212 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter; + +//import message type and parcelabe types + +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} + + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface I{{Camel .Interface.Name }}MessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class {{Camel .Interface.Name }}ServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private {{Camel .Interface.Name }}ServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private I{{Camel .Interface.Name }}EventListener testedAdapterAsEventListener; + //private ServiceController<{{Camel .Interface.Name }}ServiceAdapter> serviceController; TODO STARTED BY HAND + private Messenger mServiceMessenger; + private I{{Camel .Interface.Name }} backendServiceMock = mock(I{{Camel .Interface.Name }}.class); + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private I{{Camel .Interface.Name}}ServiceFactory serviceFactory = mock(I{{Camel .Interface.Name}}ServiceFactory.class); + private I{{Camel .Interface.Name }}MessageGetter msgMock = mock(I{{Camel .Interface.Name }}MessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, {{Camel .Interface.Name}}MessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(I{{Camel .Interface.Name }}MessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Message registerMsg = Message.obtain(null, {{Camel .Interface.Name}}MessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(msgMock); + clientReplyMessenger = new Messenger(clientReplyHandler); + + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, {{Camel .Interface.Name }}ServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService({{Camel .Interface.Name }}ServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(I{{Camel .Interface.Name }}EventListener.class); + verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + + {{- $InterfaceName := Camel .Interface.Name}} +{{- range .Interface.Properties }} + @Test + public void onReceive{{.Name}}PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue()); + Bundle data = new Bundle(); + {{- if.IsPrimitive }} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); + {{- else }} + {{javaReturn "" . }} newValue = {{javaDefault "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); + {{- end }} + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + verify(backendServiceMock,times(1)).set{{Camel .Name}}(newValue); + + } +{{- end}} +} diff --git a/templates/stub/build.gradle.tpl b/templates/stub/build.gradle.tpl index 201eae4..ce0196e 100644 --- a/templates/stub/build.gradle.tpl +++ b/templates/stub/build.gradle.tpl @@ -7,7 +7,7 @@ android { compileSdk 35 defaultConfig { - minSdk 34 + minSdk 33 targetSdk 34 versionCode 1 versionName "1.0" From 6796008efd80323157c3e97f1ae1c8804746ad74 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:10:53 +0200 Subject: [PATCH 13/45] feat(adnroid): extend tests for service add tests that actually check if service properly reacts on receiving messages and sends messages when needed. This is part of adding the android-jni feature. --- .../service/serviceadaptertest.java.tpl | 121 +++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index b21ca11..cff7ecd 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -104,7 +104,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest private String mTestConnectionID1 = "MyTestClient"; private I{{Camel .Interface.Name}}ServiceFactory serviceFactory = mock(I{{Camel .Interface.Name}}ServiceFactory.class); - private I{{Camel .Interface.Name }}MessageGetter msgMock = mock(I{{Camel .Interface.Name }}MessageGetter.class); + private I{{Camel .Interface.Name }}MessageGetter clientMessagesStorage = mock(I{{Camel .Interface.Name }}MessageGetter.class); ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); @@ -156,7 +156,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest @Before public void setUp() throws RemoteException { - clientReplyHandler = createClientHandlerMock(msgMock); + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); clientReplyMessenger = new Messenger(clientReplyHandler); mMockContext = RuntimeEnvironment.getApplication(); @@ -189,12 +189,14 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- $InterfaceName := Camel .Interface.Name}} {{- range .Interface.Properties }} +//TODO do not add when a property is readonly @Test public void onReceive{{.Name}}PropertyChangeTest() throws RemoteException { // Create and send message Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue()); Bundle data = new Bundle(); - {{- if.IsPrimitive }} + + {{- if .IsPrimitive }} {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); {{- else }} @@ -208,5 +210,118 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest verify(backendServiceMock,times(1)).set{{Camel .Name}}(newValue); } + + @Test + public void whenNotified{{.Name}}() + { + {{- if and .IsPrimitive}} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + {{- else }} + {{javaReturn "" . }} newValue = {{javaDefault "" . }}; + {{- end }} + + testedAdapterAsEventListener.on{{Camel .Name}}Changed(newValue); + Robolectric.flushForegroundThreadScheduler(); + + verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue(), response.what); + Bundle data = response.getData(); + {{- if .IsPrimitive }} + {{javaReturn "" . }} receivedByClient = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" . }} receivedByClient = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + assertEquals(receivedByClient, newValue); + } +{{- end}} + +{{- range .Interface.Signals }} + @Test + public void whenNotified{{.Name}}() + { + {{- range .Params }} + {{- if and .IsPrimitive}} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + {{- else }} + {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; + {{- end }} + {{- end }} + + testedAdapterAsEventListener.on{{Camel .Name}}({{javaVars .Params}}); + Robolectric.flushForegroundThreadScheduler(); + + verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue(), response.what); + Bundle data = response.getData(); + {{- range .Params }} + {{- if .IsPrimitive }} + {{javaReturn "" . }} receivedByClient{{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" . }} receivedByClient{{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + assertEquals(receivedByClient{{javaVar .}}, {{javaVar .}}); + {{- end}} +} +{{- end}} + + +{{- range .Interface.Operations }} + + + public void on{{.Name}}Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Req.getValue()); + Bundle data = new Bundle(); + {{- range .Params }} + {{- if .IsPrimitive }} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); + {{- else }} + {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- end }} + {{- end }} + + {{- if not .Return.IsVoid }} + {{- if and .Return.IsPrimitive}} + {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; + {{- else }} + {{javaReturn "" .Return }} returnedValue = {{javaDefault "" .Return }}; + {{- end }} + when(backendServiceMock.{{camel .Name}}({{javaVars .Params}})).thenReturn(returnedValue); + {{- end}} + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + verify(backendServiceMock,times(1)).{{camel .Name}}({{javaVars .Params}}); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + + verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + {{- if not .Return.IsVoid }} + {{- if .Return.IsPrimitive }} + {{javaReturn "" .Return }} receivedByClient = resp_data.get{{ ( Camel (javaType "" .Return) ) }}("result", -1); + {{- else }} + resp_data.setClassLoader({{Camel .Return.Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" .Return }} receivedByClient = resp_data.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); + {{- end }} + + assertEquals(receivedByClient, returnedValue); + {{- end}} + } + {{- end}} + } From 2ecd22784bc9d09108b1d222bf608f126cd3c288 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:50:51 +0200 Subject: [PATCH 14/45] feat(jni): add service backend allowing jni impl adds the version of the backend that has some native functions. This will be used with e.g. unreal template. There are no build files as unreal has its own system. This is part of adding the android-jni feature. --- rules.yaml | 14 +++ .../service/jnibridgeservice.java.tpl | 105 ++++++++++++++++++ .../service/jnibridgeservicefactory.java.tpl | 81 ++++++++++++++ .../service/jnibridgeservicestarter.java.tpl | 42 +++++++ 4 files changed, 242 insertions(+) create mode 100644 templates/jnibridge/service/jnibridgeservice.java.tpl create mode 100644 templates/jnibridge/service/jnibridgeservicefactory.java.tpl create mode 100644 templates/jnibridge/service/jnibridgeservicestarter.java.tpl diff --git a/rules.yaml b/rules.yaml index d5754b6..5aaea7d 100644 --- a/rules.yaml +++ b/rules.yaml @@ -99,3 +99,17 @@ features: documents: - source: "android/messenger/structparcelable.java.tpl" target: "{{Camel .Struct.Name }}Parcelable.java" + - name: jnibridge + requires: + - api + - android + scopes: + - match: interface + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}jniservice/" + documents: + - source: "jnibridge/service/jnibridgeservicefactory.java.tpl" + target: "{{Camel .Interface.Name}}JniServiceFactory.java" + - source: "jnibridge/service/jnibridgeservice.java.tpl" + target: "{{Camel .Interface.Name}}JniService.java" + - source: "jnibridge/service/jnibridgeservicestarter.java.tpl" + target: "{{Camel .Interface.Name }}JniServiceStarter.java" diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl new file mode 100644 index 0000000..64aa036 --- /dev/null +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -0,0 +1,105 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}jniservice; + +import android.os.Messenger; +import android.util.Log; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class {{Camel .Interface.Name}}JniService extends Abstract{{Camel .Interface.Name}} { + + + private final static String TAG = "{{Camel .Interface.Name}}JniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + +{{- range .Interface.Properties }} + @Override + public void set{{Camel .Name}}({{javaParam "" .}}) + { + Log.i(TAG, "request set{{Camel .Name}} called, will call native "); + nativeSet{{Camel .Name}}({{javaVar . }}); + } + + @Override + public {{javaReturn "" . }} get{{Camel .Name}}() + { + Log.i(TAG, "request get{{Camel .Name}} called, will call native "); + return nativeGet{{Camel .Name}}(); + } + + {{ end }} + // methods + {{- range .Interface.Operations }} + + @Override + public {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}) { + Log.w(TAG, "request method {{camel .Name}} called, will call native"); + {{if not .Return.IsVoid}}return{{ end }} native{{Camel .Name}}({{javaVars .Params }}); + } + + @Override + public {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}) { + return CompletableFuture.{{- if .Return.IsVoid }}runAsync{{else}}supplyAsync{{end}}( + () -> { {{- if not .Return.IsVoid }}return{{end}} {{camel .Name}}({{javaVars .Params }}); }, + executor); + } + + {{- end }} + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + {{- range .Interface.Properties }} + private native void nativeSet{{Camel .Name}}({{javaParam "" .}}); + private native {{javaReturn "" . }} nativeGet{{Camel .Name}}(); + {{ end }} + // methods + {{- range .Interface.Operations }} + private native {{javaReturn "" .Return}} native{{Camel .Name}}({{javaParams "" .Params}}); + {{- end }} + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + + {{- range .Interface.Properties }} + public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) + { + Log.i(TAG, "on{{Camel .Name}}Changed, will pass notification to all listeners"); + fire{{Camel .Name}}Changed(newValue); + } + {{- end}} + {{- range .Interface.Signals }} + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + Log.i(TAG, "on{{Camel .Name}}, will pass notification to all listeners"); + fire{{Camel .Name}}({{javaVars .Params}}); + } + {{- end }} + +} diff --git a/templates/jnibridge/service/jnibridgeservicefactory.java.tpl b/templates/jnibridge/service/jnibridgeservicefactory.java.tpl new file mode 100644 index 0000000..1746e4e --- /dev/null +++ b/templates/jnibridge/service/jnibridgeservicefactory.java.tpl @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package {{camel .Module.Name}}.{{camel .Module.Name}}jniservice; + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name }}ServiceFactory; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}jniservice.{{Camel .Interface.Name}}JniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton {{Camel .Interface.Name}}JniServiceFactory thread for the system. This is a thread for + * {{Camel .Interface.Name}}JniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class {{Camel .Interface.Name}}JniServiceFactory extends HandlerThread implements I{{Camel .Interface.Name }}ServiceFactory +{ + private {{Camel .Interface.Name}}JniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static {{Camel .Interface.Name}}JniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: {{Camel .Interface.Name}}JniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized Abstract{{Camel .Interface.Name }} getServiceInstance() + { + if (jniService == null) + { + jniService = new {{Camel .Interface.Name}}JniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new {{Camel .Interface.Name}}JniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final {{Camel .Interface.Name}}JniServiceFactory INSTANCE = createInstance(); + } + + private {{Camel .Interface.Name}}JniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static {{Camel .Interface.Name}}JniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + {{Camel .Interface.Name}}JniServiceFactory t = new {{Camel .Interface.Name}}JniServiceFactory(); + t.start(); + return t; + } +} diff --git a/templates/jnibridge/service/jnibridgeservicestarter.java.tpl b/templates/jnibridge/service/jnibridgeservicestarter.java.tpl new file mode 100644 index 0000000..0b335ac --- /dev/null +++ b/templates/jnibridge/service/jnibridgeservicestarter.java.tpl @@ -0,0 +1,42 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter; +import {{camel .Module.Name}}.{{camel .Module.Name}}jniservice.{{Camel .Interface.Name}}JniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class {{Camel .Interface.Name }}JniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "{{Camel .Interface.Name }}JniStarter"; + + + + public static I{{Camel .Interface.Name }} start(Context context) { + stop(context); + androidService = new Intent(context, {{Camel .Interface.Name }}ServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + {{Camel .Interface.Name}}JniServiceFactory factory = {{Camel .Interface.Name}}JniServiceFactory.get(); + Log.w(TAG, "starter: factory set for {{Camel .Interface.Name}}JniServiceFactory"); + return {{Camel .Interface.Name }}ServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file From d02200d7e1bfc5cba52d2155efcf430e0c7de40a Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:21:22 +0200 Subject: [PATCH 15/45] feat(adroid): Add client with project files adds a client adapter, that implements the ServiceConnection and the api interface. A client is generated per api interface and connects with its Service counterpart, that may be provided by diferent applications. It requires the desired package that exposes the service to be given for binding. The service must be already started - the intent does not start it. The client allows registering an event listener to get info about all the changes that service informs about. In that way it allows adding a jni version of that listener, which will be added later. This is part of adding the android-jni feature. --- rules.yaml | 17 + templates/android/client/additions.gradle.tpl | 19 + templates/android/client/build.gradle.tpl | 32 ++ templates/android/client/client.java.tpl | 386 ++++++++++++++++++ templates/android/client/clienttest.java.tpl | 0 templates/api/interface.java.tpl | 6 +- templates/settings.gradle.tpl | 1 + 7 files changed, 458 insertions(+), 3 deletions(-) create mode 100644 templates/android/client/additions.gradle.tpl create mode 100644 templates/android/client/build.gradle.tpl create mode 100644 templates/android/client/client.java.tpl create mode 100644 templates/android/client/clienttest.java.tpl diff --git a/rules.yaml b/rules.yaml index 5aaea7d..8f6cd4e 100644 --- a/rules.yaml +++ b/rules.yaml @@ -99,6 +99,23 @@ features: documents: - source: "android/messenger/structparcelable.java.tpl" target: "{{Camel .Struct.Name }}Parcelable.java" + - match: module + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" + documents: + - source: "android/client/build.gradle.tpl" + target: "build.gradle" + - source: "android/client/additions.gradle.tpl" + target: "additions.gradle" + - match: interface + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" + documents: + - source: "android/client/client.java.tpl" + target: "{{Camel .Interface.Name}}Client.java" + - match: interface + prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/src/test/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" + documents: + - source: "android/client/clienttest.java.tpl" + target: "{{Camel .Interface.Name}}ClientTest.java" - name: jnibridge requires: - api diff --git a/templates/android/client/additions.gradle.tpl b/templates/android/client/additions.gradle.tpl new file mode 100644 index 0000000..a8a8c36 --- /dev/null +++ b/templates/android/client/additions.gradle.tpl @@ -0,0 +1,19 @@ +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + implementation project(':{{camel .Module.Name}}_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/android/client/build.gradle.tpl b/templates/android/client/build.gradle.tpl new file mode 100644 index 0000000..233c3f6 --- /dev/null +++ b/templates/android/client/build.gradle.tpl @@ -0,0 +1,32 @@ +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':{{camel .Module.Name}}_api') + implementation project(':{{camel .Module.Name}}_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl new file mode 100644 index 0000000..2b5802e --- /dev/null +++ b/templates/android/client/client.java.tpl @@ -0,0 +1,386 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; + +public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface.Name}} implements ServiceConnection +{ + private static final String TAG = "{{Camel .Interface.Name }}Client"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + + {{- range .Interface.Properties }} + private {{javaReturn "" .}} m_{{javaVar .}} = {{ javaDefault "" . }}; + {{- end}} + + + public {{Camel .Interface.Name }}Client(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type {{Camel .Interface.Name }}ServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "{{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, {{Camel .Interface.Name}}MessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + doCleanupForUnbinding("unbindFromService"); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + // TODO INFORM _isReady(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + // TODO INFORM _isReady(false); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, {{Camel .Interface.Name}}MessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + + {{- $InterfaceName := Camel .Interface.Name}} + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch ({{Camel .Interface.Name}}MessageType.fromInteger(msg.what)) + { + //TODO ENUMS AND ARRAYS (for array just change the func to getXArray) + {{- range .Interface.Properties }} + case SET_{{Camel .Name}}: + { + Bundle data = msg.getData(); + {{- .IsPrimitive }} + {{javaReturn "" .}} newValue = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" .}} newValue = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + + on{{Camel .Name}}(newValue); + break; + } + {{- end }} + // TODO ENUMS AND ARRAYS (for array just change the func to getXArray) + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + {{- range .Interface.Signals }} + case SIG_{{Camel .Name}}: { + + Bundle data = msg.getData(); + {{- range .Params }} + {{- if not .IsPrimitive }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} + {{- range .Params }} + {{- if .IsPrimitive }} + {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else }} + {{javaReturn "" .}}Parcelable {{javaVar .}}Parcelable = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class); + {{javaReturn "" .}} {{javaVar .}} = {{javaVar .}}Parcelable.get{{Camel (javaReturn "" .)}}(); + {{- end }} + {{- end }} + on{{Camel .Name}}({{javaVars .Params}}); + break; + } + {{- end }} + {{- range .Interface.Operations }} + case RPC_{{Camel .Name}}Resp: { + + Bundle data = msg.getData(); + {{- range .Params }} + {{- if not .IsPrimitive }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp , could not find pending call for " + msg.obj); + } + break; + + } + {{- end }} + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + + // TODO handle arrays, enums, complex structs, imports, externs +{{- range .Interface.Properties }} + @Override + public void set{{Camel .Name}}({{javaParam "" .}}) + { + Log.i(TAG, "request set{{Camel .Name}} called "+ {{javaVar . }}); + if (m_{{javaVar .}} != {{javaVar .}}) + { + Message msg = new Message(); + msg.what = {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(); + Bundle data = new Bundle(); + {{- if .IsPrimitive }} + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar . }}); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar . }})); + {{- end }} + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void on{{Camel .Name}}({{javaParam "" .}}) + { + Log.i(TAG, "value received from service for {{Camel .Name}} "); + if (m_{{javaVar .}} != {{javaVar .}}) + { + m_{{javaVar .}} = {{javaVar .}}; + fire{{Camel .Name}}Changed({{javaVar .}}); + } + + } + + @Override + public {{javaReturn "" . }} get{{Camel .Name}}() + { + Log.i(TAG, "request get{{Camel .Name}} called, returning local"); + return m_{{javaVar .}}; + } + + {{ end }} + // methods + {{- range .Interface.Operations }} + + + @Override + public {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}) { + {{javaAsyncReturn "" .Return}} resFuture = {{camel .Name}}Async({{javaVars .Params }}); + try { + {{- if .Return.IsVoid }} + resFuture.get(); + return; + {{- else }} + return resFuture.get(); + {{- end }} + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}) { + + Log.i(TAG, "Call on service {{camel .Name}} " + {{- range .Params -}} + " " + {{javaVar .}}{{ end}}); + Message msg = new Message(); + msg.what = {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + {{- range .Params }} + {{- if .IsPrimitive }} + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{ javaVar .}}); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- end }} + {{- end }} + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + {{javaAsyncReturn "" .Return}} future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + {{- if .Return.IsVoid }} + future.complete(null); + Log.v(TAG, "resolve {{.Name }}"); + {{- else }} + {{- if (.Return.IsPrimitive) }} + {{javaReturn "" .Return }} result = bundle.get{{ ( Camel (javaType "" .Return) ) }}("result", -1); + {{- else }} + {{javaReturn "" .Return }} result = bundle.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); + {{- end }} + Log.v(TAG, "resolve {{.Name }}" + result); + future.complete(result); + {{- end }} + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + {{- end }} + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + {{- range .Interface.Signals }} + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + Log.i(TAG, "on{{Camel .Name}} received from service"); + fire{{Camel .Name}}({{javaVars .Params}}); + } + {{- end }} +} diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl new file mode 100644 index 0000000..e69de29 diff --git a/templates/api/interface.java.tpl b/templates/api/interface.java.tpl index 6ba6952..ebefcdb 100644 --- a/templates/api/interface.java.tpl +++ b/templates/api/interface.java.tpl @@ -23,9 +23,9 @@ import java.util.concurrent.CompletableFuture; {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}); {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}); {{- end }} - {{- range .Interface.Signals }} - public void fire{{Camel .Name}}({{javaParams "" .Params}}); - {{- end }} + {{- range .Interface.Signals }} + public void fire{{Camel .Name}}({{javaParams "" .Params}}); + {{- end }} boolean _isReady(); // signal listeners void addEventListener(I{{Camel .Interface.Name }}EventListener listener); diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index 936719e..c26cf85 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -22,6 +22,7 @@ dependencyResolutionManagement { rootProject.name = "{{camel .Module.Name}}" {{- if .Features.android }} include ':{{camel .Module.Name}}_android_service' +include ':{{camel .Module.Name}}_android_client' include ':{{camel .Module.Name}}_android_messenger' {{- end -}} {{- if .Features.stubs }} From 30213415747fb950d12adb68431eaf86ccfaec01 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:28:41 +0200 Subject: [PATCH 16/45] feat(andorid): add tests for client This is part of adding the android-jni feature. --- templates/android/client/clienttest.java.tpl | 298 +++++++++++++++++++ 1 file changed, 298 insertions(+) diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index e69de29..262f463 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -0,0 +1,298 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_client; + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Interface.Name }}Client; + +//import message type and parcelabe types +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface I{{Camel .Interface.Name }}ClientMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class {{Camel .Interface.Name }}ClientTest +{ + + @Mock + private Context mMockContext; + + private {{Camel .Interface.Name }}Client testedClient; + private I{{Camel .Interface.Name }}EventListener listenerMock = mock(I{{Camel .Interface.Name }}EventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + + private I{{Camel .Interface.Name }}ClientMessageGetter serviceMessagesStorage = mock(I{{Camel .Interface.Name }}ClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + {{- $InterfaceName := Camel .Interface.Name}} + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals({{$InterfaceName}}MessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(I{{Camel .Interface.Name }}ClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new {{Camel .Interface.Name }}Client(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("{{camel .Module.Name}}.{{camel .Module.Name}}_android_service", "{{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals({{$InterfaceName}}MessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + assertTrue(testedClient._isReady()); + } + +{{- range .Interface.Properties }} +//TODO do not add when a property is readonly + @Test + public void onReceive{{.Name}}PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue()); + Bundle data = new Bundle(); + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); + {{- else if (eq .KindType "bool")}} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + data.putInt("{{.Name}}", newValue); + {{- else }} + {{javaReturn "" . }} newValue = {{javaDefault "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); + {{- end }} + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + verify(listenerMock,times(1)).on{{Camel .Name}}Changed(newValue); + + } + + @Test + public void setPropertyRequest{{.Name}}() + { + {{- if and .IsPrimitive}} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + {{- else }} + {{javaReturn "" . }} newValue = {{javaDefault "" . }}; + {{- end }} + + testedClient.set{{Camel .Name}}(newValue); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(), response.what); + Bundle data = response.getData(); + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" . }} receivedByService = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else if (eq .KindType "bool")}} + {{javaReturn "" . }} receivedByService = = data.getInt("{{.Name}}", -1); + {{- else }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" . }} receivedByService = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + assertEquals(receivedByService, newValue); + } +{{- end}} + +{{- range .Interface.Signals }} + @Test + public void whenNotified{{.Name}}() throws RemoteException + { + + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue()); + Bundle data = new Bundle(); + {{- range .Params }} + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); + {{- else if (eq .KindType "bool")}} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.putInt("{{.Name}}", {{javaVar .}}); + {{- else }} + {{javaReturn "" . }} {{javaVar . }} = {{javaDefault "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- end }} + {{- end }} + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + verify(listenerMock,times(1)).on{{Camel .Name}}({{javaVars .Params}}); + +} +{{- end}} + + +{{- range .Interface.Operations }} + + + public void on{{.Name}}Request() throws RemoteException { + + // Execute method + {{- range .Params }} + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + {{- else if (eq .KindType "bool")}} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + {{- else }} + {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; + {{- end }} + {{- end }} + + {{- if and .Return.IsPrimitive}} + {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; + {{- else }} + {{javaReturn "" .Return }} expectedResult = {{javaDefault "" .Return }}; + {{- end }} + + AtomicBoolean receivedResp = new AtomicBoolean(false); + {{javaAsyncReturn "" .Return}} resFuture = testedClient.{{camel .Name}}Async({{javaVars .Params}}); + + resFuture.thenAccept(result -> { + {{- if and (.Return.IsPrimitive) (not (eq .Return.KindType "string")) }} + assertEquals(expectedResult, result.{{camel (javaType "" .Return)}}Value()); + {{- else }} + assertEquals(expectedResult, result); + {{- end }} + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + Message method_request = messageCaptor.getValue(); + assertEquals({{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Req.getValue(), method_request.what); + + Bundle req_data = method_request.getData(); + {{- range .Params }} + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" . }} received{{javaVar .}} = req_data.get{{ ( Camel (javaType "" .) ) }}("result", -1); + {{- else if (eq .KindType "bool")}} + {{javaReturn "" . }} received{{javaVar .}} = = req_data.getInt("{{javaVar .}}", -1); + {{- else }} + req_data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" . }} received{{javaVar .}} = req_data.getParcelable("result", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + assertEquals(received{{javaVar .}}, {{javaVar .}}); + {{- end }} + int returnedCallId = req_data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue()); + + Bundle data = new Bundle(); + data.putInt("callId", returnedCallId); + {{- if not .Return.IsVoid }} + {{- if .Return.IsPrimitive }} + data.put{{ ( Camel (javaType "" .Return) ) }}("result",expectedResult); + {{- else }} + data.putParcelable("result", new {{Camel .Return.Type}}Parcelable(expectedResult)); + {{- end }} + {{- end }} + + msg.setData(data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +{{- end}} + +} From 3ad626f02a7733a41a585ecd9f81b1ef792b2804 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:04:37 +0200 Subject: [PATCH 17/45] feat: extend event listener adding new event and a way to fire it to get info about object isReady state change (e.g may be used for connection established or backend ready). This is part of adding the android-jni feature. --- templates/android/client/client.java.tpl | 5 ++--- templates/android/client/clienttest.java.tpl | 9 +++++++-- templates/android/service/serviceadapter.java.tpl | 10 +++++++++- templates/api/abstract.java.tpl | 6 ++++++ templates/api/eventlistener.java.tpl | 1 + templates/api/interface.java.tpl | 1 + templates/jnibridge/service/jnibridgeservice.java.tpl | 5 +++++ templates/stub/implservice.java.tpl | 6 +++++- 8 files changed, 36 insertions(+), 7 deletions(-) diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 2b5802e..ded7153 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -109,7 +109,6 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface msg.getData().putString("connectionID", mConnectionId); mClientHandler.sendToService(msg); mApplicationContext.unbindService(this); - doCleanupForUnbinding("unbindFromService"); } } @@ -122,7 +121,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface mIsBoundToService = true; requestRegisterClient(); - // TODO INFORM _isReady(true); + fire_readyStatusChanged(true); } @Override @@ -130,7 +129,6 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface { Log.w(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); - // TODO INFORM _isReady(false); } @Override @@ -157,6 +155,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface mServiceMessenger = null; mClientMessenger = null; mIsBoundToService = false; + fire_readyStatusChanged(false); } diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 262f463..17f97e2 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -74,6 +74,7 @@ public class {{Camel .Interface.Name }}ClientTest private Handler mServiceHandler ; private String mTestConnectionID1 = "MyTestClient"; InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; private I{{Camel .Interface.Name }}ClientMessageGetter serviceMessagesStorage = mock(I{{Camel .Interface.Name }}ClientMessageGetter.class); @@ -92,6 +93,8 @@ public class {{Camel .Interface.Name }}ClientTest assertEquals({{$InterfaceName}}MessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + testedClient.removeEventListener(listenerMock); } @@ -111,6 +114,7 @@ public class {{Camel .Interface.Name }}ClientTest public void setUp() throws RemoteException { inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); mServiceMessenger = new Messenger(mServiceHandler); IBinder serviceBinder = mServiceMessenger.getBinder(); @@ -130,6 +134,7 @@ public class {{Camel .Interface.Name }}ClientTest mClientMessenger = register_msg.replyTo; assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); assertTrue(testedClient._isReady()); } @@ -154,7 +159,7 @@ public class {{Camel .Interface.Name }}ClientTest msg.setData(data); mClientMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - verify(listenerMock,times(1)).on{{Camel .Name}}Changed(newValue); + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(newValue); } @@ -210,7 +215,7 @@ public class {{Camel .Interface.Name }}ClientTest msg.setData(data); mClientMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - verify(listenerMock,times(1)).on{{Camel .Name}}({{javaVars .Params}}); + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}({{javaVars .Params}}); } {{- end}} diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index 54e4230..851704e 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -320,6 +320,14 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service sendMessageToActivityClients(msg); } {{- end }} - + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } } } diff --git a/templates/api/abstract.java.tpl b/templates/api/abstract.java.tpl index bf38ca5..adf1c7e 100644 --- a/templates/api/abstract.java.tpl +++ b/templates/api/abstract.java.tpl @@ -40,4 +40,10 @@ import java.util.HashSet; } {{ end }} + public void fire_readyStatusChanged(boolean isReady) + { + for ({{$interfaceName}}EventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } } diff --git a/templates/api/eventlistener.java.tpl b/templates/api/eventlistener.java.tpl index f51741e..e149fe4 100644 --- a/templates/api/eventlistener.java.tpl +++ b/templates/api/eventlistener.java.tpl @@ -14,4 +14,5 @@ import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; {{- range .Interface.Signals }} void on{{Camel .Name}}({{javaParams "" .Params}}); {{- end }} + void on_readyStatusChanged(boolean isReady); } diff --git a/templates/api/interface.java.tpl b/templates/api/interface.java.tpl index ebefcdb..2134b66 100644 --- a/templates/api/interface.java.tpl +++ b/templates/api/interface.java.tpl @@ -28,6 +28,7 @@ import java.util.concurrent.CompletableFuture; {{- end }} boolean _isReady(); // signal listeners + public void fire_readyStatusChanged(boolean isReady); void addEventListener(I{{Camel .Interface.Name }}EventListener listener); void removeEventListener(I{{Camel .Interface.Name }}EventListener listener); } diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl index 64aa036..6a68298 100644 --- a/templates/jnibridge/service/jnibridgeservice.java.tpl +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -31,6 +31,11 @@ public class {{Camel .Interface.Name}}JniService extends Abstract{{Camel .Interf private static boolean isServiceReady = false; private static final ExecutorService executor = Executors.newFixedThreadPool(1); + public {{Camel .Interface.Name}}JniService() + { + fire_readyStatusChanged(true); + } + {{- range .Interface.Properties }} @Override public void set{{Camel .Name}}({{javaParam "" .}}) diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index c29002f..cc8779d 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -26,7 +26,6 @@ import java.util.function.Supplier; public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface.Name}} { - private final static String TAG = "{{Camel .Interface.Name}}Service"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done private static final ExecutorService executor = Executors.newFixedThreadPool(1); @@ -35,6 +34,11 @@ public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface private {{javaReturn "" .}} m_{{javaVar .}} = {{ javaDefault "" . }}; {{- end}} + public {{Camel .Interface.Name}}Service() + { + fire_readyStatusChanged(true); + } + {{- range .Interface.Properties }} @Override public void set{{Camel .Name}}({{javaParam "" .}}) From e237095fbf639372ad9ad964a1867c6df1c09b6b Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:37:41 +0200 Subject: [PATCH 18/45] feat(example) add client example adds an almost ready to start application for test purposes. IMPORTANT it generates the functionality only for first interface. That is for usability purpose, some tabs could be introduced for each interface, but this make example more complex. It genereates buttons to request some propety changes: IMPORTANT the logic how to change it needs to be implemented by user, similar for executing methods, the user must provide proper parameters. It prints the result of events received from service: property changes, singals receivied, method results. This is part of adding the android-jni feature. --- rules.yaml | 76 ++++++ templates/settings.gradle.tpl | 3 + .../testclientapp/AndroidManifest.xml.tpl | 27 ++ templates/testclientapp/application.java.tpl | 255 ++++++++++++++++++ templates/testclientapp/build.gradle.tpl | 41 +++ templates/testclientapp/proguard-rules.pro | 21 ++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 +++ .../res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../testclientapp/res/values-night/themes.xml | 16 ++ templates/testclientapp/res/values/colors.xml | 10 + .../testclientapp/res/values/strings.xml.tpl | 3 + templates/testclientapp/res/values/themes.xml | 16 ++ .../testclientapp/res/xml/backup_rules.xml | 13 + .../res/xml/data_extraction_rules.xml | 19 ++ 26 files changed, 712 insertions(+) create mode 100644 templates/testclientapp/AndroidManifest.xml.tpl create mode 100644 templates/testclientapp/application.java.tpl create mode 100644 templates/testclientapp/build.gradle.tpl create mode 100644 templates/testclientapp/proguard-rules.pro create mode 100644 templates/testclientapp/res/drawable/ic_launcher_background.xml create mode 100644 templates/testclientapp/res/drawable/ic_launcher_foreground.xml create mode 100644 templates/testclientapp/res/mipmap-anydpi/ic_launcher.xml create mode 100644 templates/testclientapp/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 templates/testclientapp/res/mipmap-hdpi/ic_launcher.webp create mode 100644 templates/testclientapp/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 templates/testclientapp/res/mipmap-mdpi/ic_launcher.webp create mode 100644 templates/testclientapp/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 templates/testclientapp/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 templates/testclientapp/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 templates/testclientapp/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 templates/testclientapp/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 templates/testclientapp/res/values-night/themes.xml create mode 100644 templates/testclientapp/res/values/colors.xml create mode 100644 templates/testclientapp/res/values/strings.xml.tpl create mode 100644 templates/testclientapp/res/values/themes.xml create mode 100644 templates/testclientapp/res/xml/backup_rules.xml create mode 100644 templates/testclientapp/res/xml/data_extraction_rules.xml diff --git a/rules.yaml b/rules.yaml index 8f6cd4e..9e7057d 100644 --- a/rules.yaml +++ b/rules.yaml @@ -130,3 +130,79 @@ features: target: "{{Camel .Interface.Name}}JniService.java" - source: "jnibridge/service/jnibridgeservicestarter.java.tpl" target: "{{Camel .Interface.Name }}JniServiceStarter.java" + - name: testclientapp + requires: + - api + - android + scopes: + - match: module + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/" + documents: + - source: "testclientapp/build.gradle.tpl" + target: "build.gradle" + - match: module + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/src/main/" + documents: + - source: "testclientapp/AndroidManifest.xml.tpl" + target: "AndroidManifest.xml" + - source: "testclientapp/application.java.tpl" + target: "java/{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/{{Camel .Module.Name}}TestClientApp.java" + - source: "testclientapp/res/drawable/ic_launcher_background.xml" + target: "res/drawable/ic_launcher_background.xml" + raw: true + - source: "testclientapp/res/drawable/ic_launcher_foreground.xml" + target: "res/drawable/ic_launcher_foreground.xml" + raw: true + - source: "testclientapp/res/values/colors.xml" + target: "res/values/colors.xml" + raw: true + - source: "testclientapp/res/values/strings.xml.tpl" + target: "res/values/strings.xml" + - source: "testclientapp/res/values/themes.xml" + target: "res/values/themes.xml" + raw: true + - source: "testclientapp/res/values-night/themes.xml" + target: "res/values-night/themes.xml" + raw: true + - source: "testclientapp/res/xml/backup_rules.xml" + target: "res/xml/backup_rules.xml" + raw: true + - source: "testclientapp/res/xml/data_extraction_rules.xml" + target: "res/xml/data_extraction_rules.xml" + raw: true + - source: "testclientapp/res/mipmap-anydpi/ic_launcher.xml" + target: "res/mipmap-anydpi/ic_launcher.xml" + raw: true + - source: "testclientapp/res/mipmap-anydpi/ic_launcher_round.xml" + target: "res/mipmap-anydpi/ic_launcher_round.xml" + raw: true + - source: "testclientapp/res/mipmap-hdpi/ic_launcher.webp" + target: "res/mipmap-hdpi/ic_launcher.webp" + raw: true + - source: "testclientapp/res/mipmap-hdpi/ic_launcher_round.webp" + target: "res/mipmap-hdpi/ic_launcher_round.webp" + raw: true + - source: "testclientapp/res/mipmap-mdpi/ic_launcher.webp" + target: "res/mipmap-mdpi/ic_launcher.webp" + raw: true + - source: "testclientapp/res/mipmap-mdpi/ic_launcher_round.webp" + target: "res/mipmap-mdpi/ic_launcher_round.webp" + raw: true + - source: "testclientapp/res/mipmap-xhdpi/ic_launcher.webp" + target: "res/mipmap-xhdpi/ic_launcher.webp" + raw: true + - source: "testclientapp/res/mipmap-xhdpi/ic_launcher_round.webp" + target: "res/mipmap-xhdpi/ic_launcher_round.webp" + raw: true + - source: "testclientapp/res/mipmap-xxhdpi/ic_launcher.webp" + target: "res/mipmap-xxhdpi/ic_launcher.webp" + raw: true + - source: "testclientapp/res/mipmap-xxhdpi/ic_launcher_round.webp" + target: "res/mipmap-xxhdpi/ic_launcher_round.webp" + raw: true + - source: "testclientapp/res/mipmap-xxxhdpi/ic_launcher.webp" + target: "res/mipmap-xxxhdpi/ic_launcher.webp" + raw: true + - source: "testclientapp/res/mipmap-xxxhdpi/ic_launcher_round.webp" + target: "res/mipmap-xxxhdpi/ic_launcher_round.webp" + raw: true diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index c26cf85..0dc106f 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -31,3 +31,6 @@ include ':{{camel .Module.Name}}_impl' {{- if .Features.api }} include ':{{camel .Module.Name}}_api' {{- end -}} +{{- if .Features.testclientapp }} +include ':{{dot .Module.Name}}_client_example' +{{- end -}} diff --git a/templates/testclientapp/AndroidManifest.xml.tpl b/templates/testclientapp/AndroidManifest.xml.tpl new file mode 100644 index 0000000..18c0102 --- /dev/null +++ b/templates/testclientapp/AndroidManifest.xml.tpl @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/templates/testclientapp/application.java.tpl b/templates/testclientapp/application.java.tpl new file mode 100644 index 0000000..caaf5c0 --- /dev/null +++ b/templates/testclientapp/application.java.tpl @@ -0,0 +1,255 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added +{{- $Interface := (index .Module.Interfaces 0) }} + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel $Interface.Name }}Client; + +//import message type and parcelabe types +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; +import java.util.concurrent.CompletableFuture; + + + +public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{Camel $Interface.Name }}EventListener +{ + + private static final String TAG = "{{Camel .Module.Name}}TestClientApp"; + + private {{Camel $Interface.Name }}Client mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "{{dot .Module.Name}}.{{dot .Module.Name}}_service_example.{{Camel .Module.Name}}TestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + {{- range $Interface.Properties }} + Button b{{Camel .Name}} = new Button(this); + b{{Camel .Name}}.setText("Set {{.Name}}"); + b{{Camel .Name}}.setBackgroundColor(Color.GREEN); + + b{{Camel .Name}}.setOnClickListener(v -> { + {{javaReturn "" . }} new{{Camel .Name}} = {{ if or .IsPrimitive (eq .KindType "enum") -}} + mClient.get{{Camel .Name}}(); + {{ else -}} + new {{javaReturn "" . }}(mClient.get{{Camel .Name}}();); + {{- end }} + //TODO increment + Log.i(TAG, "SET {{.Name}}" + new{{Camel .Name}}); + mClient.set{{Camel .Name}}(new{{Camel .Name}}); + }); + propertyButtonsLine.addView(b{{Camel .Name}}); + {{- end }} + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + {{- range $Interface.Operations }} + Button b{{Camel .Name}} = new Button(this); + b{{Camel .Name}}.setText("{{.Name}}"); + + b{{Camel .Name}}.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD {{.Name}} "); + {{- range .Params}} + {{javaType "" . }} {{javaVar .}} = {{javaTestValue "" .}}; + {{- end}} + {{javaAsyncReturn "" .Return}} method_res + = mClient.{{camel .Name}}Async({{javaVars .Params }}).thenApply( + i -> { + outputTextVieMethodRes.setText("Got {{.Name}} result "+ i); + return i; + }); + }); + b{{Camel .Name}}.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(b{{Camel .Name}}); + {{- end }} + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new {{Camel $Interface.Name }}Client(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + + {{- range $Interface.Properties }} + @Override + public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) + { + outputTextViewProp.setText("Property from service: {{.Name}} " + newValue); + Log.w(TAG, "Property from service: {{.Name}} " + newValue); + } + {{- end }} + {{- range $Interface.Signals }} + @Override + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + String text = "Signal {{.Name}} {{- range .Params -}}" + " " + {{javaVar .}}{{ end}}; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + {{- end }} + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/templates/testclientapp/build.gradle.tpl b/templates/testclientapp/build.gradle.tpl new file mode 100644 index 0000000..397e103 --- /dev/null +++ b/templates/testclientapp/build.gradle.tpl @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace '{{dot .Module.Name}}.{{dot .Module.Name}}_client_example' + compileSdk 35 + + defaultConfig { + applicationId "{{dot .Module.Name}}.{{dot .Module.Name}}_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':{{camel .Module.Name}}_api') + implementation project(':{{camel .Module.Name}}_android_messenger') + implementation project(':{{camel .Module.Name}}_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/templates/testclientapp/proguard-rules.pro b/templates/testclientapp/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/templates/testclientapp/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/templates/testclientapp/res/drawable/ic_launcher_background.xml b/templates/testclientapp/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/templates/testclientapp/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/testclientapp/res/drawable/ic_launcher_foreground.xml b/templates/testclientapp/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/templates/testclientapp/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/templates/testclientapp/res/mipmap-anydpi/ic_launcher.xml b/templates/testclientapp/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/templates/testclientapp/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/testclientapp/res/mipmap-anydpi/ic_launcher_round.xml b/templates/testclientapp/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/templates/testclientapp/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/testclientapp/res/mipmap-hdpi/ic_launcher.webp b/templates/testclientapp/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/templates/testclientapp/res/mipmap-hdpi/ic_launcher_round.webp b/templates/testclientapp/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/templates/testclientapp/res/mipmap-mdpi/ic_launcher.webp b/templates/testclientapp/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/templates/testclientapp/res/mipmap-xhdpi/ic_launcher.webp b/templates/testclientapp/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/templates/testclientapp/res/mipmap-xhdpi/ic_launcher_round.webp b/templates/testclientapp/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} zs4%E?srLW`CJh0GCIK@hTkrW7A15Iu%N&?Q^$0+!{Tv&|t^Y@u%!L zglTg&?Q5q#ijZ;&HBQ?FNPp;k3J5!&{^+SGq?AX~SiOM9jJMRpyP?RCr@z38AQyy&WRMaC;n4una$~nJKSp?q|s8F00c9?Q! zY_ovvjTFm+DeQM^LXJ#v0}6HRt3R1%5PT*}W!k8BEM;Jrj8dIceFo2fhzTqaB3KKk zGlCLI)gU25(#u6ch6GeB1k@eHq7l{EHXv0n6xE#ws#ri}08kkCf8hUt{|Ejb`2YW* zvg}0nSSX1m=76s?sZhRY$K=3dpJ+y*eDULGnL2}4>4nvW^7_<~wIM_5fjvwt4h1|g z)g0Z6ZFq9j<~9~b8((~TN{Z?ZQfw|is&Xp~AC61sj;xItKyCHdI|tCMC_LbXF>~vR z=w6V3^H=W4CbAgR4#xw}ETTwu2guW~=Crl@SMXv85jQ=%y!s^?m4PI0My7MWICO;- z175jm%&PcPWh8QdOU(#8bp4!N7ET-+)N}N2zk2)8ch|4Q&lPFNQgT-thu053`r*h3 z_8dI@G;`zn;lH$zX3RzIk`E8~`J=BBdR}qD%n@vVG1834)!pS1Y?zVkJGtsa(sB~y zNfMYKsOJb%5J(0ivK8d+l2D2y&5X!cg3BG!AJ}910|_${nF}sC1QF^nLIhzXk-Y#x z0)&1iK!O;Og0Ky!;`b~v%b$`S4E&fB)1NB4v@8wr( z&+NX4e^&o)ecb=)dd~C!{(1e6t?&9j{l8%U*k4)?`(L3;Qjw z#w7FS+U(94MaJKS!J9O8^$)36_J8;thW#2$y9i{bB{?M{QS_inZIJ!jwqAbfXYVd$ zQ5fC$6Nc9hFi8m^;oI-%C#BS|c8vy+@{jx6hFcf^_;2VRgkoN(0h!_VSGmgNPRsxI z8$rTo0LaYq-H5i&gtj81=&xU?H-Y2==G@uQV7E`@+2E9XQW@{&j`?EOktk|Ho{HU>ZqDzvgjwBmdex z&uZNd2C1h{{}2k6Ys9$*nFP3;K%u!MhW`uZy7Sn`1M1zs@Es&;z*Z>Gsh@-3Fe6pE zQD2@cqF((NrRevgvLsvM_8;;iNyJ5nyPyy?e!kvKjGj`6diRFBEe49Oa7wwkJFV7Z z$YT&DWloYu-H?3<0BKn9L&JYDT-SK~*6c5pi18P26$JESKRYj{T7Zk6KiRJcbvOO*{P56Q6s8msbeI3>|j>K9}Q9UBeq*inXKemCm`-<5|-$ZyN4u$(3 z&HcvqehFD%5Yrmykg-^d`=BSa8(i=>ZoC77^mWY{evp(km@aHqhUECBz76YiR+VYK zY_avFC~V3$=`6C4JhfHAQ@DZtUOwH`L;oYX6zK0-uI^?hS$ALfq}A7evR;ohJHij} zHSZdW?EKv9U1s4oD*<(0oQ*;MaQ6@cvGL zuHCPgm_NhVsgp^sfr*ia^Db}swo1?O(_Q2)y+S$CBm+g=9wCOUPbz(x)_GbaKa@A7 zuI&!ynLiZRT#V%_y_-D`0Z5lT*auoe{(U5NylTzFSJW()W-#F6*&A`LNO1bV#Y;QJ zSbLBnp|B^dtK|KIWC|No>JjWBWE@n7O)x{&^E(WMeMvp57#qA8m* zeTow*U@_86B#Fm*rxyYu5PRWaWHx8y> z*qmHEp(AMDl0v)ij(AY8fnH=~ZwwjVAbu*m5;xPfidh@ov6d8g zfJsi&!QyK53Es%sC39ts;54V68koALD4b|%tNHW0bIkZAJKa=W&FomJSEDT>W1xIX z1x%Z>AvNIsSPLcn3RTcHXb@KB?cuM)=x6fcIx>&(GxqZ8w3p#jJ(GVgc*`c0HG}dv zIop&Qim!K1NFwic%07KcjWgHBPUkq7f~lj;TPqVGTiT#cUeim>;nY`>h@a*S{qQex zQ`z62WK|Mj)Y{tfF{;T4P;c8$Q|KU?Joh zIkA^z%X7z|r>4aTh@|StTi!-r1D!g=zb#3d#{{&K3CqE$Iz-UH<%37c zRfkO`&uM%#AD3PHv`g5t0e^O%nVL0d{Xlx^EjEC3#skF@`zl-7PF^0oxW)1!C!JxR zWvuAHH?)61FKA1QeT*_sY7;_Id#!GmV4n`MO{~sv}VLSK` zXRw=Y=Clz*00B(5y^K;gCZMAzjT5+c3IC=)l(9VIDdatpxj3y89WwI|bH&$!ZEvp` zPR!T@#!(|KfI-w?!&+7$N3F6>tD{YO4Qg$d_`nNEdfVCha9vaPn0jI0`)`@*72hq! zpU5ND^P*RoEkbD5o#az(-g=Y)L>HH>Oc%}$ zT3Rs_ih0;4+Lv4Y;@Iv(;fUbQ=i-G(#>vghec~*j(I#r|5mqFiJBpzi&hzEcD{u$< zRsm0BVYn=pT;0>R(itW|*D&;O%bOc7et9ACaH#J>z3A1A~6fdP>pmbM%xzm4>|;c_?B+%sl;Qs2{t!60$^u zH1t@9^6>;?!FuusnISi$f5CL&;z?EqJN$FBuWDA#D5`cy_UvCFIVvf{c?4N0teh;d zET$7aVbj08KTQS!x?Nd1Is8q8qFzs}a=!@nJ;7FSfCY^T@D-gpw`w<6e#X3+;O}1h z$%I!M)0bg|EKUA04Qjn@+x{Rj8vt6Wn!R|3A92z}^$KfF5(#CWr4y#~re1CN4i4w0 z#GsypBR{xA3Er7sgAi(|}1-W?s~n$7?K|9WL8kpVfw-;#b9 z+mn;=ep!162U5R>_t}fOt~tE?s#m( zO-S$7>Ay6*hHdZ)7_oU915WYYCIX;hFI-U2EWYX!pllONr@Q--2o~`!isi6vTPLJ4@(|o=%NHYjo0_S&q*UQIROw@*N-By@PaQ&;YxFZ0aR zX&}LeOEz);#m~Hwm^VAY8DK}b$F4bo{jMN?d!lxKPhNklzr^Cd`0f4oJr^z=I|l`* zm8AHm*fPV`0=lF3Pnnp}&J0N1X@}-D94YvmUabFrLGSnTz7Mu^21F#O5tN#CuY9Vh zUZBH=ez%h*wkf0hBtXJh1SN3d+IF{gzT7lp)j}n?03lt;XSQRAh7qd&v;RwTYDuQ# zbI2*r<>?x-G0@hM{;%{VBD7nLKt~D`T~-HAt5;h%i0_=Ifs=yHma5dhJ+QMG?Ux(a z|E?1CMy1!~oA`FP!k~iG=t&5#>bVdz=peT8HMB6Y)#7PpETtNryT^+Rv3vpJaF^zP z{H}0-LyV9Fu21ID%wO9f1IKlFr1p4c{o-?03vyB-tr5duk^&L$;m_|f$vs`^Sl{j2 z95}oY{LlY+=ZS%J+tZoXCd0*sSU7w^gjovXn+g7uyra5{cU49@yHf#Z^Jl-$9cIfo z+AJuxH$VLb=#+uBbVmUjnx zxb1pZ@-O9=AIk4@S)m6fJ2?{HrNYwwnL3a45muuNjr;6$O`bGEM0T4A2_S$t=86*- zcO+0mywg*j#A4mU}enR_!cGmIYQ;qwfchWtFEXL)AK%*;=j znYne+hS4EMy3S)C*mZ1KI>!+)0V@9!N6H$Y}~MJ{rYuf zz^KljIWvFi-?#?V@LPR&c6Nn{!=XM z>}-h$S76;$H{E{Y%@^zlmOl^efBwa%UU+jJD9UVukQ3ti_kH-?H*RC0?M1W%FCvMB zM_+v6fk$6X2sx)-p~B3&Kl{nscK}pNLM*qjtpaf9>AU{-iPKQZR8yCg!TY}Qg*(;) z)gdvCcB%kppZc$VdvsK@)3l1{&DG!d_6OHOS`y=ITLEVu`unSKA2E%JD*DVX{LJ}K z9l>hMRDqxQh0lnpGHpVYneX}eA3Pt|2v%=q;rt)``R|#bDyB)OXY&vI_@|*}h}G?^ z@aZ4_!7cQPX`!fW_?{oT1NTwHs#l5L-0`E|y@48<3Q^HFf8=Idi zpJYD%1MkII!~|7I^WGo)IF=?{>ACnjJ_WUi39C}!Q{QnheVJqeKKqq5^o5CBde(g9 zvw$X6^jz_^E2$wSw4!q5*RG(C2_^XO$HBn_55vbl44OnTTRwRaePP0vo{K)U1#99& z<>rq7V&V(<&@I%MFoN5zrY}sz=(*-L&}1QQ*a%`u25h{cFj===17eB_uGuzG&byQ< zrm8BJZl4r_E$3k|Wo6FW0-6M7>qac5uFQsQcmkLWGfeH74S3Z_rJ!jgN++!@i=HW8 zkyjI(oPH-+-N#Qc^-mpNO`bc6r=2-<%&Wy5K1vfFJB(L_IkpS6fY^NmuL8qsgj>MD zn~BHH9WM~32_3vd=W&B)k7F9q%stJx+b_L_X-4zr^LVUMCmyCTA3sWtkvsmME?Xiy z?xOSfB=_$oY06~J-HcCq&)qcW{j;uP;?Dm}=hkq?zh&n!;m((-G-u_t|6x399Q;>A zgNpxoJNj{u|MFDH7Rhq@FCAl0dE|ddnl!oh9{Lq?@JDoR6L;C941IK`ISfdE$4S zE0AUQ8+2|Ncl_q5QkSp#AODp~(^mfP&%Au@@|TBQwoP`UU+V{6u8|)6ZA{~uKmQ*M zmrMTDU8S~8Eqi{^v0Ug&5Upcm#y7Z1(RbgZAG8jB$eRwCspQ)>5;U)oGZ&E5aeR*K z8Yt`Y0$G))Yd(Y3KH}tA4`-_QmNke5hU_|nq=xtyjwW(_o?itz>B>WM&^63bNdQ)k@-IgDHW*RW$Xo9#RzrTrCn7L2H{9Amq|qNg@#eZY=|P zCoI?2s+L)zsM%WX(NbVEY^`C>lFjIBYmJ6@DKJ0ZT4&F&WHW!dwa%QzOG!?jY_2(S zDcEzZbz*2Q!43|z))9yOP9X1Xt%DXzwY(3tl-TR=Qb_MbZYRrooh;dYYmS!U_as1(=YVB?Q_A|tNu5Ut&_q3jbfDM zoFxT^uEuH`nX3*sB%K?GuHUkweYReBwnHqh3P)~`+s3+Tj!rDA1e)8vuBv5J*IsxC zkd^~b(aGzArj08{>cnzOuy04C+C`}gb|Yz-1avxeWzev3NzcHbz_&4W@QCr$z3~w=8Ua- z`;vfG1~BP8CyLb=F7t1am~ph_#|O%$khSJ9%Vtcn)YmpgQxF?xM^_Vb+5fnpB^W0I`f%X8gb9#X{Q-yJG0{Z56aWeI&zPxnf5pdJA38bM`cYnS#x)% z`n1tFf$i)W-hGm(f9mde^=X@NcV_lFb=P`4&CI&H=IArijGwdCk&X@uQ$5xmj!~^? z#$ROCI)V-~t%L%GS#wo@U27ddR`4`3)WoB{R-4snfNrfee|kI8^bu#yDgYqOwas9# zmcb`3!kRJ`Cr=_tq)8aMt{aGtUZsqwVlj6DgCGre>AEt&x8H_in!x@uwgExIh|-mA zjdaC(29~CTVSaaF7HPbql&*9Uo8P@f)>LqCXclr}peS7_1BQ28u9PO8Eq1@`l3q9o zkfKCaO2?T?ZyA6loW<#9_c^O=m<&h}CA!ineAD@=(gbq`vyT|tiJ6#^B1$P;;qax` z55k&Q?wEh#87niLo*+n4L@65J(Nz~=Ya%7^(miLb(E>A3B@|Jjl;FU&D>o|9#7PJH z?|ago!o;WC^h=|T7PVBg(DAB}72cyUS zb(f>Bwbr!F1eTCO5fpj<{PqhY5>143p?~5ZA5H40);=@M#MYvrB6gqHbU_!GSY??i z%s=>-ciA4*zOOZHds0a(kWewZ4h(k8h(ua7HX)Au&mY~H8KY6(_cb$_&fA@QjIW-*heP3%$d!m5^AdnT}`12qA^c@!g3DOwZ5WwE2?)-yU z!)Vx#Mtxt?FzFTwK!77sy7)sMzUd->w4^bxtpM2j!b1pjgyk zGKwWGeb4)^zjy{9Es&PU1}gwg?|J#L$KJB7ett9@4M%-nGtIQr0>Fl@8-yh`-+1ed zS6r}(MeSvgSoFmH*_WPu@i?}!AB~2?;i&IxrkNg~cQ9Som98tcq)k^|eeER|Zl77t za-TVUc;DNvzVXJ%w52+#weN?+;i#{f#!Oc&z?81*N>^e~ltRS%ZI@lR{rs()HmqG! zx*}ZrI-EZ}ckJMiy>A^oofwDfC~IH)z8{VHKGT@#E5I(Ll&+MnMCl>~AV7+>Gi%mF zkU1QlKASdR0B80!YhP<$Ywi0?W2Ux45oPfxv9QolWzJPD^weBfvo4SONxP35106sAmh(e+vAs0GboFD@PvNs)jNPvarhW}0YliZEg{Gazv z+JDIpoojRVPr<*C|BTq<`6ga{5q^8^!|0cxe=rZ!zxH3%f5ZO0cQ*Z<^$Yt2{|Ek0 zyT|*F+CO@K;(owBKtGg!S^xj-Z~rga2m6nxKl9J=fBSuNKW_dLKWhJKeg^-Xe`^1? z`TyJj)8E!#>_3Y?uKrwqq3LJ#SGU>AzUO|6`nR^u&3FNN_jGOc zw)Nw`wr3yIKhgcee6IaN=ws>M{6677%)hPwx&HzC(f&u~&)6@b2kNRzBDQAP0*H73 zq%McOmRk{B3i47qRe=DA*$&odrbEJZ*pV9XXa&p@wlW~@Yfs>V{yiTtplMhgM*-Bz zsSnlq&pG;z0OUN%$~$3=g1UF+G*>+17eRbBf3=y79J}KR8owon@$1Z7MIrvvWWH)34nK2SD)GsrJ{l z1Cl#oVo3A8qY3e=aF)qzms~FG#2$LzT=gs&aVMOj>(%{y<&O0cG!nCiESl~x=^dF{ zKvj8F1K8Ng171wwM5Fh4KoQw`_c6#y$(5cAm7e}~nJ#A*fx+c9;y#&W!#VukR)ugk zKp3=+;Ut+IYn%m+r4d*<`L2h%aDnX5}^!5R|H;(34AoVWjRx(msBZvk;rCI*|~ zdOijqI@9Z{Vu!~jvHW{lBa$rnl4+!s_5sfK3bCGk-B%iDe&@-}+%fOKU|(9?V1 zHE8&@4z)Kx!RAvAs z!Wic9=o#(bg?kc-G68-m(jZ`^=XGUXb)}t(%&~sjFnV^sEX%hSy6UKC4iOhgV=BHV z2w`4g7Y=s#Vu2B_?#VQ|hP39@eArgfX>-0S+dd&^mx0*wp}>)x;c4RUgxz%;oNe?& z-7-lJ@Y^2^C;=qJsxx5|xF)*pTGhch2B&kxtn;f!7=gznk}I3}Dh}(CoMXgA5-p&kS202!l?!fT3t|HG*rIP~mS* z$Wjo}jq3}z$Qq!9yrtd3fM0N629ZM?LU$nv@Tv9b7I;D|;0H2dsA~g7Z7zp1| zB)XmrkMgF6OQr|R)HHD^TE{Y#j!~SR?b`Xt3Qs`B+x<hxexYeAjMUWdZ-*n9%(1)Wb(n2U<><7&9dwGJmrob)4%H? zlQ%z+L-^$dFhhH|@u$%97Qz?*Ynh2VG@q|?8vY&L74&fs&_b&3$x&Oyjl~LQDRRap zJU4U*R+(2Dd!G+lh8!V{pT_UJn+^1Qg6$` zqkNm(a#hWyc6SP+p5=C4HL8-m`pO`5o~`-LI?_h5CsH?F_%?nDodmz&pWR20WTpJE z?N|wSzLjMUK8E)a2tI}Lf;+;*M|h3Y(U#>)g1>zk9|Hd}oZAa2 zLYBWBoSW!Ts!RwXr^8h+U*@{9{zqS^iH)Op<;r`Uw~nc}<^$V~_i%$GFjaG?X1@E|M`h)nekvFKt`Dh-f>@|0-`Xoq)o` zx;JmzDfOV9qCx|EVpogEe0LK~tGS?5$$L_i6P$P6wIsCQaP_;d{{N=iV@+8LI}o#( zvo*Ejy=IIn{rdIQh1&q-{EuohpVOjJ^Q3lD*YTp37$^RRgn8ihpdu5{Ct%5-KO!VL zcNB6dUajXI9jkm-P|i3~GB-A(X`P1Oqqb$tcku)UJw0w3GeUijb__#QT4j%64z%EeB7S?jlWwx_7&+EEvB|6N=kV}DwnyAlX=?j`) zmU#!$*^@NIu#n_d7;WoJV@*Fbv9|yJO4;n|BNF2xy(54RyB>t~8lUOUW$&2%Nwi1y zx6JxW88>U2$#qhl^6KUbtmg9}D0o5vYDT7kWJthLGkpGnN4T>{St^_EU>4;DmLF9o zr|LqsA8_MoNLQ=}w?8u!ziSZ@PC#Y<#9uJFo-ozVo6D;<8j^1$c|qAE3ZTE5i~zmE z$BU5lw6l=EWsg^y^;8>r9qH{xfL|~PZYK#md$zZ0?o11gV<*WSW~cgy2GYGQir%wf zt4iW8D+;s*;RGrmd(-T<@2&j(Cb9xhV*l-x`TpK`xq|7p?5R%5*s!69?2c!cC*VY* z2DE^9pvOPLU!1e}wA8S8opcTJ3`NB>hY=JQnL~QFXR4K8A$BqJnoEB$wn-%u@E6Mh zCfMF4kusv3N!(aHC}4)Xs^xoOwXd%e^6pi5|DZo=Q25j+6HlJ^7FodH6y1bMROR^q zGu6)fopS`h%Sw<;ZH%TEPf+#81-#_v+@8nlR0jLcIDKQtLleOC)6yLZgC!D9X3GgS zohwU{v$jl=quD#Go^hB{`@Qw*a%`(^jyT~=q^bWgGzRj;|12J55HWdCWV}EB|K=%N z3Nq-qxJJ`>^|1MNN+q}zTB&ooE3j==AgK@^UW<^oSbeALa2peF)Th6{@sj0KyMNHZ zksk1+MXN2tv+22A%cQOGpS9)77(uP9mh+!5T5ERLvF@b}$+WvXM45Z?-kCa)fb~f1 znVbTD$Gx-0Zxc`0D@YgHakge6SL0H`-vN_x?AP0>iGH0_EE&=v83hMJgaKAI0jJXm zVxVz;X<$v6WW7}fxROO7vr#YLP;;lij5VrX{;>7kK6TtOH&6|Ar^xo>00%+u$C4@# z>!jOt6*3><171+WxoZnKDTzJtDRw+T030;yI}~uV@9fCnei^I*j>Bp&mzP2d=FPb_ zCM*l_+$LDR3B*a!A$g#>xsrZvw0lckxmMg>0aQd7tPyN=t{dgXb;Ie+T8{fZH=gdu zM7Rg9c(kg(Jg0?ARRRl=AONFKrvFj)lTY$KfT%6^6s`mk*ABGhsce*LsoD>K{z_M2 ziPpnu+lw22PfF!CoId^6n*G4H(Ix+#+N{C(da7t1BYMGEaE#PdpOLxsVD5riQXHp@OX;`S`8VnpM~)I920w~<3|mo0 zf8~Az`*?2?H&gZ&*K&bRkV@qzvMlRHXys8*Ze2+1c?5o!^+$&MHxB@4Ee5cke52R! zmn7AZtY6ST%ixgU5)%$%QcwHj7Es-Qu^kLAPwy%7pGBw_4Q9#da^W2$}axNHr03)_nw z5?yuNmXrI5HgS46)c5&}B)Tts49oU92>3xBLLy}FMUW=84DQbVq^;7_e7|(Sdz|&J z73N+M`rc2rt*oSWu#7S{*s~nH6HRHJS1SmzeXk|;CA)FI4bat3<%}nkB%;;?=F>B7ms9QSxv#@+69;@>QaR?REYX4&)=itG>rM{<{A79Rmk)`5ON#GL`*KX%}Ihk3w(RtM-WLt z?f&FLF}4N^yE!(pZ&Yj&Bc`~K0@4_}*0Om?wN|}4WJ>WL;G^H2*QpgEkGA~OET-Km zkwz|5{6dnz1U<2Pe9DNL>3g5FEIvp1jzP&2K#z~j%g6!7B;^zF+o95?fV{3mnB8*RMhCDNp>Am-3e@jNfMj?jHV$MWjk!DDKP zkAz$Y?Sr)!GUOX}qTQ5aMh|wq1uq}~joWyKl=b_LboM#wi{CMuz5x6BKlA-qy++cM01D3b7`uD z#l6M4pI;JCypO8JZ6?U&wNxR!{4oB_ zlV!x9+-&Qy6{%MQ{~yoZGkKiTSC`YS_j22~G;xUV855g2&C(zm^V!(wpcm@zn{%!g z4}JGo(sGZ1O~to-}le

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/templates/testclientapp/res/values-night/themes.xml b/templates/testclientapp/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/templates/testclientapp/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/templates/testclientapp/res/values/colors.xml b/templates/testclientapp/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/templates/testclientapp/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/templates/testclientapp/res/values/strings.xml.tpl b/templates/testclientapp/res/values/strings.xml.tpl new file mode 100644 index 0000000..7147c01 --- /dev/null +++ b/templates/testclientapp/res/values/strings.xml.tpl @@ -0,0 +1,3 @@ + + {{Camel .Module.Name}}TestClientApp + \ No newline at end of file diff --git a/templates/testclientapp/res/values/themes.xml b/templates/testclientapp/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/templates/testclientapp/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/templates/testclientapp/res/xml/backup_rules.xml b/templates/testclientapp/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/templates/testclientapp/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/templates/testclientapp/res/xml/data_extraction_rules.xml b/templates/testclientapp/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/templates/testclientapp/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file From 1ad83b339b811b0c2e3280ab69b52c4c043732b1 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:17:00 +0200 Subject: [PATCH 19/45] feat(adnroid): sync initial state of service side add message and handling that allow synchronizing the client state with the service state on the connection established. The service sends all the values of properties. This is part of adding the android-jni feature. --- templates/android/client/client.java.tpl | 22 ++++++- templates/android/client/clienttest.java.tpl | 29 +++++++++ templates/android/messenger/messages.java.tpl | 3 +- .../android/service/serviceadapter.java.tpl | 35 ++++++++-- .../service/serviceadaptertest.java.tpl | 65 +++++++++++++++---- 5 files changed, 132 insertions(+), 22 deletions(-) diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index ded7153..0417419 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -187,7 +187,27 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface switch ({{Camel .Interface.Name}}MessageType.fromInteger(msg.what)) { - //TODO ENUMS AND ARRAYS (for array just change the func to getXArray) + case INIT: + { + Bundle data = msg.getData(); + {{range .Interface.Properties}} + {{- if not .IsPrimitive }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} + {{range .Interface.Properties}} + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); + {{- else if (eq .KindType "bool")}} + {{javaReturn "" .}} {{javaVar .}} = = data.getInt}("{{.Name}}", -1); + {{- else }} + {{javaReturn "" .}} {{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} + on{{Camel .Name}}({{javaVar .}}); + {{- end}} + + break; + } {{- range .Interface.Properties }} case SET_{{Camel .Name}}: { diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 17f97e2..4760417 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -138,6 +138,35 @@ public class {{Camel .Interface.Name }}ClientTest assertTrue(testedClient._isReady()); } + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.INIT.getValue()); + Bundle data = new Bundle(); + {{- range .Interface.Properties}} + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); + {{- else if (eq .KindType "bool")}} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.putInt("{{.Name}}", {{javaVar .}}); + {{- else }} + {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- end }} + {{- end }} + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + {{- range .Interface.Properties}} + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed({{javaVar .}}); + {{- end }} + } + {{- range .Interface.Properties }} //TODO do not add when a property is readonly @Test diff --git a/templates/android/messenger/messages.java.tpl b/templates/android/messenger/messages.java.tpl index 50d8fa0..a675e59 100644 --- a/templates/android/messenger/messages.java.tpl +++ b/templates/android/messenger/messages.java.tpl @@ -5,7 +5,8 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; public enum {{Camel .Interface.Name}}MessageType { REGISTER_CLIENT(0), UNREGISTER_CLIENT(1), - {{- $msgNum := 1}} + INIT(2), + {{- $msgNum := 2}} {{- range .Interface.Properties }} {{- $msgNum = len (printf "%*s " $msgNum "" ) }} PROP_{{Camel .Name}}({{$msgNum}}), diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index 851704e..515f9c1 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -137,7 +137,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service class IncomingHandler extends Handler implements I{{Camel .Interface.Name }}EventListener { private final Service mApplicationContext; - private final ConcurrentHashMap mActivityClients = new ConcurrentHashMap<>(); + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); IncomingHandler(Service context) { @@ -145,9 +145,9 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service mApplicationContext = context; } - private void sendMessageToActivityClients(Message msg) + private void sendMessageToClients(Message msg) { - for (Map.Entry client : mActivityClients.entrySet()) + for (Map.Entry client : mClients.entrySet()) { Messenger reply = client.getValue(); if (reply != null) @@ -181,6 +181,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service { case REGISTER_CLIENT: addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); break; case UNREGISTER_CLIENT: removeClientActivity(msg.getData().getString("connectionID")); @@ -273,17 +274,37 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service { if (serviceReply != null) { - mActivityClients.put(connectionID, serviceReply); + mClients.put(connectionID, serviceReply); Log.i(TAG, "Register event listener with connectionID = " + connectionID); } } private void removeClientActivity(String connectionID) { - mActivityClients.remove(connectionID); + mClients.remove(connectionID); Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); } + private void sendInit() + { + Message msg = new Message(); + msg.what = {{$InterfaceName}}MessageType.INIT.getValue(); + Bundle data = new Bundle(); + //TODO ArrayProperties, enums + {{range .Interface.Properties}} + {{javaReturn "" .}} {{javaVar .}} = mBackendService.get{{Camel .Name}}(); + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); + {{- else if (eq .KindType "bool")}} + data.putInt("{{.Name}}", {{javaVar .}}); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- end }} + {{- end}} + msg.setData(data); + sendMessageToClients(msg); + } + {{- range .Interface.Properties }} @Override public void on{{Camel .Name}}Changed({{javaType "" .}} newValue){ @@ -298,7 +319,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); {{- end }} msg.setData(data); - sendMessageToActivityClients(msg); + sendMessageToClients(msg); } {{- end }} {{- range .Interface.Signals }} @@ -317,7 +338,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service {{- end }} {{- end }} msg.setData(data); - sendMessageToActivityClients(msg); + sendMessageToClients(msg); } {{- end }} @Override diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index cff7ecd..72e63b6 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -59,7 +59,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.Mockito.*; import static org.junit.Assert.*; - import androidx.annotation.NonNull; import org.junit.After; @@ -69,6 +68,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.android.controller.ServiceController; @@ -83,6 +83,7 @@ interface I{{Camel .Interface.Name }}MessageGetter { public void getMessage(Message msg); } +{{- $InterfaceName := Camel .Interface.Name}} @Config(sdk = 33, manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) @@ -95,9 +96,10 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest private {{Camel .Interface.Name }}ServiceAdapter testedServiceAdapter; private Intent testedServiceAdapterIntent; private I{{Camel .Interface.Name }}EventListener testedAdapterAsEventListener; - //private ServiceController<{{Camel .Interface.Name }}ServiceAdapter> serviceController; TODO STARTED BY HAND private Messenger mServiceMessenger; private I{{Camel .Interface.Name }} backendServiceMock = mock(I{{Camel .Interface.Name }}.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; private Handler clientReplyHandler ; private Messenger clientReplyMessenger; @@ -124,7 +126,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest mMockContext.stopService(testedServiceAdapterIntent); } testedServiceAdapter.onDestroy(); - verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); } Handler createClientHandlerMock(I{{Camel .Interface.Name }}MessageGetter messageGetterMock) @@ -141,6 +143,15 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest void registerFakeActivityClient(Messenger messenger, String id) { + {{- range .Interface.Properties}} + {{- if .IsPrimitive }} + when(backendServiceMock.get{{Camel .Name}}()).thenReturn({{javaTestValue "" .}}); + {{- else }} + when(backendServiceMock.get{{Camel .Name}}()).thenReturn({{javaDefault "" .}}); + {{- end }} + {{- end }} + + Message registerMsg = Message.obtain(null, {{Camel .Interface.Name}}MessageType.REGISTER_CLIENT.ordinal()); registerMsg.getData().putString("connectionID", id); registerMsg.replyTo = messenger; @@ -151,6 +162,31 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest throw new RuntimeException(e); } Robolectric.flushForegroundThreadScheduler(); + + {{- range .Interface.Properties}} + inOrderBackendService.verify(backendServiceMock, times(1)).get{{Camel .Name}}(); + {{- end }} + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + {{- range .Interface.Properties}} + {{- if not .IsPrimitive }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} + {{- range .Interface.Properties}} + assertTrue(data.containsKey("{{.Name}}")); + {{- if .IsPrimitive }} + assertEquals(data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1), {{javaTestValue "" .}}); + {{- else }} + // TODO uncomment after adding comparision operator + // assertEquals(data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(), {{javaDefault "" .}}); + {{- end }} + {{- end }} + } @Before @@ -158,7 +194,8 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest { clientReplyHandler = createClientHandlerMock(clientMessagesStorage); clientReplyMessenger = new Messenger(clientReplyHandler); - + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); mMockContext = RuntimeEnvironment.getApplication(); when(backendServiceMock._isReady()).thenReturn(true); @@ -179,7 +216,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest testedServiceAdapter.setService(serviceFactory); // service adapter should pass its member to backend to get notifications on changes. ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(I{{Camel .Interface.Name }}EventListener.class); - verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); testedAdapterAsEventListener = eventListnerCaptor.getValue(); // Register a fake client (the message handler form its service-connection) @@ -187,7 +224,6 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } - {{- $InterfaceName := Camel .Interface.Name}} {{- range .Interface.Properties }} //TODO do not add when a property is readonly @Test @@ -207,7 +243,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest msg.setData(data); mServiceMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - verify(backendServiceMock,times(1)).set{{Camel .Name}}(newValue); + inOrderBackendService.verify(backendServiceMock,times(1)).set{{Camel .Name}}(newValue); } @@ -223,7 +259,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest testedAdapterAsEventListener.on{{Camel .Name}}Changed(newValue); Robolectric.flushForegroundThreadScheduler(); - verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); assertEquals({{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue(), response.what); @@ -253,16 +289,20 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest testedAdapterAsEventListener.on{{Camel .Name}}({{javaVars .Params}}); Robolectric.flushForegroundThreadScheduler(); - verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); assertEquals({{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue(), response.what); Bundle data = response.getData(); + {{- range .Params}} + {{- if not .IsPrimitive }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} {{- range .Params }} {{- if .IsPrimitive }} {{javaReturn "" . }} receivedByClient{{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); {{- else }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); {{javaReturn "" . }} receivedByClient{{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); {{- end }} assertEquals(receivedByClient{{javaVar .}}, {{javaVar .}}); @@ -300,12 +340,11 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest msg.setData(data); mServiceMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - verify(backendServiceMock,times(1)).{{camel .Name}}({{javaVars .Params}}); + inOrderBackendService.verify(backendServiceMock,times(1)).{{camel .Name}}({{javaVars .Params}}); //Now verify it was sent back to caller Robolectric.flushForegroundThreadScheduler(); - - verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); assertEquals({{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue(), response.what); From 586c86d9deb9c13fda14ad037fc951a3ef6eeea4 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:32:59 +0200 Subject: [PATCH 20/45] feat(android): support enums add parcelable wrappers for enums and add proper handling of them in service and client. IMPORTANT: it is not yet handled if enum is part of struct. This is part of adding the android-jni feature. --- rules.yaml | 5 ++ templates/android/client/client.java.tpl | 1 + templates/android/client/clienttest.java.tpl | 17 +++++-- .../android/messenger/enumparcelable.java.tpl | 51 +++++++++++++++++++ .../android/service/serviceadapter.java.tpl | 1 + .../service/serviceadaptertest.java.tpl | 21 +++++--- templates/api/enum.java.tpl | 42 +++++++-------- 7 files changed, 107 insertions(+), 31 deletions(-) create mode 100644 templates/android/messenger/enumparcelable.java.tpl diff --git a/rules.yaml b/rules.yaml index 9e7057d..ad98e50 100644 --- a/rules.yaml +++ b/rules.yaml @@ -99,6 +99,11 @@ features: documents: - source: "android/messenger/structparcelable.java.tpl" target: "{{Camel .Struct.Name }}Parcelable.java" + - match: enum + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/" + documents: + - source: "android/messenger/enumparcelable.java.tpl" + target: "{{Camel .Enum.Name }}Parcelable.java" - match: module prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" documents: diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 0417419..b77c4f4 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -24,6 +24,7 @@ import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name {{- end }} {{- range .Module.Enums }} import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 4760417..4ff2734 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -152,6 +152,9 @@ public class {{Camel .Interface.Name }}ClientTest {{- else if (eq .KindType "bool")}} {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; data.putInt("{{.Name}}", {{javaVar .}}); + {{- else if eq .KindType "enum"}} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); {{- else }} {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); @@ -174,12 +177,15 @@ public class {{Camel .Interface.Name }}ClientTest // Create and send message Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue()); Bundle data = new Bundle(); - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); {{- else if (eq .KindType "bool")}} {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; data.putInt("{{.Name}}", newValue); + {{- else if eq .KindType "enum"}} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); {{- else }} {{javaReturn "" . }} newValue = {{javaDefault "" . }}; data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); @@ -195,7 +201,7 @@ public class {{Camel .Interface.Name }}ClientTest @Test public void setPropertyRequest{{.Name}}() { - {{- if and .IsPrimitive}} + {{- if or .IsPrimitive (eq .KindType "enum") }} {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; {{- else }} {{javaReturn "" . }} newValue = {{javaDefault "" . }}; @@ -235,6 +241,9 @@ public class {{Camel .Interface.Name }}ClientTest {{- else if (eq .KindType "bool")}} {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; data.putInt("{{.Name}}", {{javaVar .}}); + {{- else if eq .KindType "enum"}} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); {{- else }} {{javaReturn "" . }} {{javaVar . }} = {{javaDefault "" . }}; data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); @@ -257,7 +266,7 @@ public class {{Camel .Interface.Name }}ClientTest // Execute method {{- range .Params }} - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} + {{- if or (and (.IsPrimitive) (not (eq .KindType "bool")) ) (eq .KindType "enum") }} {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; {{- else if (eq .KindType "bool")}} {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; @@ -266,7 +275,7 @@ public class {{Camel .Interface.Name }}ClientTest {{- end }} {{- end }} - {{- if and .Return.IsPrimitive}} + {{- if or .Return.IsPrimitive (eq .Return.KindType "enum") }} {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; {{- else }} {{javaReturn "" .Return }} expectedResult = {{javaDefault "" .Return }}; diff --git a/templates/android/messenger/enumparcelable.java.tpl b/templates/android/messenger/enumparcelable.java.tpl new file mode 100644 index 0000000..7748c78 --- /dev/null +++ b/templates/android/messenger/enumparcelable.java.tpl @@ -0,0 +1,51 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Enum.Name}}; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class {{Camel .Enum.Name}}Parcelable implements Parcelable { + + public {{Camel .Enum.Name}} data; + + public {{Camel .Enum.Name}}Parcelable({{Camel .Enum.Name}} data) { + this.data = data; + } + + public {{Camel .Enum.Name}} get{{Camel .Enum.Name}}() + { + return data; + } + + protected {{Camel .Enum.Name }}Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = {{Camel .Enum.Name}}.fromValue(intValue); + } + + public static final Creator<{{Camel .Enum.Name }}Parcelable> CREATOR = new Creator<{{Camel .Enum.Name}}Parcelable>() { + @Override + public {{Camel .Enum.Name}}Parcelable createFromParcel(Parcel in) { + return new {{Camel .Enum.Name}}Parcelable(in); + } + + @Override + public {{Camel .Enum.Name}}Parcelable[] newArray(int size) { + return new {{Camel .Enum.Name}}Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index 515f9c1..3195158 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -21,6 +21,7 @@ import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name {{- end }} {{- range .Module.Enums }} import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 72e63b6..1cbcba2 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -144,7 +144,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest void registerFakeActivityClient(Messenger messenger, String id) { {{- range .Interface.Properties}} - {{- if .IsPrimitive }} + {{- if or .IsPrimitive (eq .KindType "enum" ) }} when(backendServiceMock.get{{Camel .Name}}()).thenReturn({{javaTestValue "" .}}); {{- else }} when(backendServiceMock.get{{Camel .Name}}()).thenReturn({{javaDefault "" .}}); @@ -181,7 +181,9 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest assertTrue(data.containsKey("{{.Name}}")); {{- if .IsPrimitive }} assertEquals(data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1), {{javaTestValue "" .}}); - {{- else }} + {{- else if eq .KindType "enum" }} + // assertEquals(data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(), {{javaTestValue "" .}}); + {{- else }} // TODO uncomment after adding comparision operator // assertEquals(data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(), {{javaDefault "" .}}); {{- end }} @@ -235,7 +237,11 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- if .IsPrimitive }} {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); - {{- else }} + data.putInt("{{.Name}}", newValue); + {{- else if (eq .KindType "enum") }} + {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); + {{- else }} {{javaReturn "" . }} newValue = {{javaDefault "" . }}; data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); {{- end }} @@ -250,7 +256,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest @Test public void whenNotified{{.Name}}() { - {{- if and .IsPrimitive}} + {{- if or .IsPrimitive (eq .KindType "enum" ) }} {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; {{- else }} {{javaReturn "" . }} newValue = {{javaDefault "" . }}; @@ -279,7 +285,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest public void whenNotified{{.Name}}() { {{- range .Params }} - {{- if and .IsPrimitive}} + {{- if or .IsPrimitive (eq .KindType "enum" ) }} {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; {{- else }} {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; @@ -322,7 +328,10 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- if .IsPrimitive }} {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); - {{- else }} + {{- else if eq .KindType "enum" }} + {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; + data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); + {{- else }} {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); {{- end }} diff --git a/templates/api/enum.java.tpl b/templates/api/enum.java.tpl index 0957f17..759cabd 100644 --- a/templates/api/enum.java.tpl +++ b/templates/api/enum.java.tpl @@ -2,29 +2,29 @@ package {{dot .Module.Name}}.{{dot .Module.Name}}_api; import com.fasterxml.jackson.annotation.JsonProperty; - public enum {{Camel .Enum.Name}} { - {{- range $idx, $m :=.Enum.Members }} - {{- if $idx}}, {{ end -}} - @JsonProperty("{{.Value}}") - {{Camel .Name}}({{.Value}}) - {{- end }}; +public enum {{Camel .Enum.Name}} +{ + {{ range $idx, $m :=.Enum.Members }} + {{- if $idx}}, + {{ end -}} + @JsonProperty("{{.Value}}") + {{Camel .Name}}({{.Value}}) +{{- end -}}; - private final int value; + private final int value; - {{Camel .Enum.Name}}(int value) { - this.value = value; - } - - public int getValue() { - return value; - } + {{Camel .Enum.Name}}(int value) { + this.value = value; + } - public static {{Camel .Enum.Name}} fromValue(int value) { - for ({{Camel .Enum.Name}} e : values()) { - if (e.value == value) return e; - } - throw new IllegalArgumentException("Unknown code: " + code); + public int getValue() { + return value; } - } -} \ No newline at end of file + public static {{Camel .Enum.Name}} fromValue(int value) { + for ({{Camel .Enum.Name}} e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} From 0d83e7af6f32fbad6adee622837333683f1a85f1 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:15:34 +0200 Subject: [PATCH 21/45] feat(api): add copy ctor for structs In java the structs (java objects) are passed by ref. The copy constructor helps proper managing of lifetime when handling with Parcelable objects. This is preparatory commit to be used for helpers when handling arrays of structures. This is part of adding the android-jni feature. --- templates/api/struct.java.tpl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/templates/api/struct.java.tpl b/templates/api/struct.java.tpl index 65b9256..1448a7f 100644 --- a/templates/api/struct.java.tpl +++ b/templates/api/struct.java.tpl @@ -17,4 +17,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; @JsonProperty("{{snake .Name}}") public {{javaType "" .}} {{camel .Name}}; {{- end }} + + public {{Camel .Struct.Name}}({{Camel .Struct.Name}} other) { +{{- range .Struct.Fields }} + this.{{camel .Name}} = other.{{camel .Name}}; + {{- end }} + //TODO deepcopy of structs and arrays this.x = new sth(other.x); +} + } From 103a44dcb47c7d1de6ea42ccf562f938f8b78cc2 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:16:39 +0200 Subject: [PATCH 22/45] feat(android): add helpers for handlig arrays Add functions for (de)serializing arrays of parcelable elements. This is part of adding the android-jni feature. --- .../android/messenger/enumparcelable.java.tpl | 18 +++++++++++++++++ .../messenger/structparcelable.java.tpl | 20 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/templates/android/messenger/enumparcelable.java.tpl b/templates/android/messenger/enumparcelable.java.tpl index 7748c78..900ac6a 100644 --- a/templates/android/messenger/enumparcelable.java.tpl +++ b/templates/android/messenger/enumparcelable.java.tpl @@ -44,6 +44,24 @@ import android.os.Parcelable; dest.writeInt(data.getValue()); } + public static {{Camel .Enum.Name }}Parcelable[] wrapArray({{Camel .Enum.Name }}[] enums) { + if (enums == null) return null; + {{Camel .Enum.Name }}Parcelable[] result = new {{Camel .Enum.Name }}Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new {{Camel .Enum.Name }}Parcelable(enums[i]); + } + return result; + } + + public static {{Camel .Enum.Name }}[] unwrapArray({{Camel .Enum.Name }}Parcelable[] parcelables) { + if (parcelables == null) return null; + {{Camel .Enum.Name }}[] out = new {{Camel .Enum.Name }}[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].get{{Camel .Enum.Name}}(); + } + return out; + } + @Override public int describeContents() { return 0; diff --git a/templates/android/messenger/structparcelable.java.tpl b/templates/android/messenger/structparcelable.java.tpl index 67fb916..c455f3a 100644 --- a/templates/android/messenger/structparcelable.java.tpl +++ b/templates/android/messenger/structparcelable.java.tpl @@ -18,7 +18,7 @@ import android.os.Parcelable; public {{Camel .Struct.Name}} get{{Camel .Struct.Name}}() { - return data; + return new {{Camel .Struct.Name}}(data); } protected {{Camel .Struct.Name}}Parcelable(Parcel in) { @@ -58,8 +58,24 @@ import android.os.Parcelable; {{- end }} // TODO arrays in general // TODO add enums - // TODO write other structs same as enums - they all should be parcelable + } + public static {{Camel .Struct.Name}}Parcelable[] wrapArray({{Camel .Struct.Name}}[] structs) { + if (structs == null) return null; + {{Camel .Struct.Name}}Parcelable[] out = new {{Camel .Struct.Name}}Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new {{Camel .Struct.Name}}Parcelable(structs[i]); + } + return out; + } + + public static {{Camel .Struct.Name}}[] unwrapArray({{Camel .Struct.Name}}Parcelable[] parcelables) { + if (parcelables == null) return null; + {{Camel .Struct.Name}}[] out = new {{Camel .Struct.Name}}[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].get{{Camel .Struct.Name}}(); + } + return out; } @Override From db97dc3991485f17f2b564b5a0a0abce9540d8b0 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:18:23 +0200 Subject: [PATCH 23/45] feat(android): handle arrays data types Adds handling of arrays of all types: simple types, enums, structs. adds some go templates for easier handling same code in many places. Add handling of arrays of any type for messanger in service. NOTE: using template makes the code not aligned This is part of adding the android-jni feature. --- templates/android/client/client.java.tpl | 90 ++++---- templates/android/client/clienttest.java.tpl | 194 ++++++++++-------- .../android/service/serviceadapter.java.tpl | 59 ++---- .../service/serviceadaptertest.java.tpl | 138 ++++++------- 4 files changed, 237 insertions(+), 244 deletions(-) diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index b77c4f4..832e0b9 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -40,6 +40,48 @@ import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.concurrent.atomic.AtomicInteger; + +{{- define "getDataFromBundle"}} + {{- if .IsPrimitive }} + {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}"{{if not .IsArray}}, {{javaDefault "" .}}{{end}}); + {{- else if .IsArray }} + {{javaReturn "" .}} {{javaVar .}} = {{Camel .Type}}Parcelable.unwrapArray(({{Camel .Type}}Parcelable[])data.getParcelableArray("{{.Name}}", {{Camel .Type}}Parcelable.class)); + {{- else }} + {{javaReturn "" .}} {{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} +{{- end }} + +{{- define "putDataIntoBundle"}} + {{- if and .IsPrimitive }} + data.put{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}", {{ javaVar .}}); + {{- else if .IsArray }} + data.putParcelableArray("{{.Name}}", {{Camel (javaElementType "" .) }}Parcelable.wrapArray({{javaVar .}})); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel (javaElementType "" .) }}Parcelable({{javaVar .}})); + {{- end }} +{{- end }} + +{{- define "getResultFromBundle"}} + {{- if and .Return.IsPrimitive }} + {{javaReturn "" .Return }} result = bundle.get{{ ( Camel (javaElementType "" .Return ) ) }}{{if .Return.IsArray}}Array{{end}}("result"{{if not .Return.IsArray}}, {{javaDefault "" .Return}}{{end}}); + {{- else if .Return.IsArray }} + {{javaReturn "" .Return}} result = {{Camel (javaElementType "" .Return) }}Parcelable.unwrapArray(({{Camel .Return.Type}}Parcelable[])bundle.getParcelableArray("result", {{Camel .Return.Type}}Parcelable.class)); + {{- else }} + {{javaReturn "" .Return }} result = bundle.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); + {{- end }} +{{- end }} + +{{- define "putResultIntoBundle"}} + {{- if and .Return.IsPrimitive }} + resp_data.put{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result", result); + {{- else if .Return.IsArray }} + resp_data.putParcelableArray("result",{{Camel (javaElementType "" .Return) }}Parcelable.wrapArray(result)); + {{- else }} + resp_data.putParcelable("result", new {{Camel (javaElementType "" .Return) }}Parcelable(result)); + {{- end }} +{{- end }} + + public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface.Name}} implements ServiceConnection { private static final String TAG = "{{Camel .Interface.Name }}Client"; @@ -197,13 +239,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface {{- end }} {{- end }} {{range .Interface.Properties}} - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else if (eq .KindType "bool")}} - {{javaReturn "" .}} {{javaVar .}} = = data.getInt}("{{.Name}}", -1); - {{- else }} - {{javaReturn "" .}} {{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); - {{- end }} + {{template "getDataFromBundle" . }} on{{Camel .Name}}({{javaVar .}}); {{- end}} @@ -213,18 +249,17 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface case SET_{{Camel .Name}}: { Bundle data = msg.getData(); - {{- .IsPrimitive }} - {{javaReturn "" .}} newValue = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else }} + + {{- if not .IsPrimitive }} data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" .}} newValue = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); - {{- end }} + {{- end }} + + {{template "getDataFromBundle" . }} - on{{Camel .Name}}(newValue); + on{{Camel .Name}}({{javaVar .}}); break; } {{- end }} - // TODO ENUMS AND ARRAYS (for array just change the func to getXArray) // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. @@ -238,12 +273,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface {{- end }} {{- end }} {{- range .Params }} - {{- if .IsPrimitive }} - {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else }} - {{javaReturn "" .}}Parcelable {{javaVar .}}Parcelable = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class); - {{javaReturn "" .}} {{javaVar .}} = {{javaVar .}}Parcelable.get{{Camel (javaReturn "" .)}}(); - {{- end }} + {{template "getDataFromBundle" . }} {{- end }} on{{Camel .Name}}({{javaVars .Params}}); break; @@ -282,7 +312,6 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface } }; - // TODO handle arrays, enums, complex structs, imports, externs {{- range .Interface.Properties }} @Override public void set{{Camel .Name}}({{javaParam "" .}}) @@ -293,11 +322,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface Message msg = new Message(); msg.what = {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(); Bundle data = new Bundle(); - {{- if .IsPrimitive }} - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar . }}); - {{- else }} - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar . }})); - {{- end }} + {{template "putDataIntoBundle" . }} msg.setData(data); mClientHandler.sendToService(msg); } @@ -355,11 +380,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface int msgId = callIdsGetter.getAndIncrement(); data.putInt("callId",msgId); {{- range .Params }} - {{- if .IsPrimitive }} - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{ javaVar .}}); - {{- else }} - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- end }} + {{template "putDataIntoBundle" . }} {{- end }} msg.setData(data); msg.replyTo = mClientMessenger; @@ -367,16 +388,11 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface {{javaAsyncReturn "" .Return}} future = new CompletableFuture<>(); Consumer resolver = bundle -> { - {{- if .Return.IsVoid }} future.complete(null); Log.v(TAG, "resolve {{.Name }}"); {{- else }} - {{- if (.Return.IsPrimitive) }} - {{javaReturn "" .Return }} result = bundle.get{{ ( Camel (javaType "" .Return) ) }}("result", -1); - {{- else }} - {{javaReturn "" .Return }} result = bundle.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); - {{- end }} + {{template "getResultFromBundle" . }} Log.v(TAG, "resolve {{.Name }}" + result); future.complete(result); {{- end }} diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 4ff2734..d680f92 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -59,6 +59,42 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter public void getMessage(Message msg); } +{{- define "getReceivedFromBundle"}} + {{- if .IsPrimitive }} + {{javaReturn "" .}} received{{javaVar .}} = data.get{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}"{{if not .IsArray}}, {{javaDefault "" .}}{{end}}); + {{- else if .IsArray }} + {{javaReturn "" .}} received{{javaVar .}} = {{Camel .Type}}Parcelable.unwrapArray(({{Camel .Type}}Parcelable[])data.getParcelableArray("{{.Name}}", {{Camel .Type}}Parcelable.class)); + {{- else }} + {{javaReturn "" .}} received{{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{- end }} +{{- end }} + +{{- define "putTestDataIntoBundle"}} + {{- if .IsPrimitive }} + data.put{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}", test{{ javaVar .}}); + {{- else if .IsArray }} + data.putParcelableArray("{{.Name}}", {{Camel (javaElementType "" .) }}Parcelable.wrapArray(test{{javaVar .}})); + {{- else }} + data.putParcelable("{{.Name}}", new {{Camel (javaElementType "" .) }}Parcelable(test{{javaVar .}})); + {{- end }} +{{- end }} + +{{- define "prepareTestValue"}} + {{- if .IsArray }} + {{javaElementType "" .}} element{{ javaVar .}} = {{javaTestValue "" . }}; + // todo fill if is struct + {{javaReturn "" . }} test{{ javaVar .}} = new {{javaReturn "" . }}{element{{ javaVar .}}} ; + {{- else if (.IsPrimitive) }} + {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; + {{- else if eq .KindType "enum"}} + {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; + {{- else }} + {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; + //TODO fill fields + {{- end }} +{{- end }} + + @Config(sdk = 33, manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class {{Camel .Interface.Name }}ClientTest @@ -146,19 +182,8 @@ public class {{Camel .Interface.Name }}ClientTest Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.INIT.getValue()); Bundle data = new Bundle(); {{- range .Interface.Properties}} - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); - {{- else if (eq .KindType "bool")}} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.putInt("{{.Name}}", {{javaVar .}}); - {{- else if eq .KindType "enum"}} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- else }} - {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- end }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} {{- end }} //setup mock expectations @@ -166,7 +191,11 @@ public class {{Camel .Interface.Name }}ClientTest mClientMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); {{- range .Interface.Properties}} - inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed({{javaVar .}}); + {{- if or (.IsPrimitive) (eq .KindType "enum") }} + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(test{{javaVar .}}); + {{- else }} + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(any({{javaReturn "" . }}.class)); + {{- end }} {{- end }} } @@ -177,37 +206,25 @@ public class {{Camel .Interface.Name }}ClientTest // Create and send message Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue()); Bundle data = new Bundle(); - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); - {{- else if (eq .KindType "bool")}} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - data.putInt("{{.Name}}", newValue); - {{- else if eq .KindType "enum"}} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); - {{- else }} - {{javaReturn "" . }} newValue = {{javaDefault "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); - {{- end }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} msg.setData(data); mClientMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(newValue); - + {{- if or (.IsPrimitive) (eq .KindType "enum") }} + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(test{{javaVar .}}); + {{- else }} + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(any({{javaReturn "" . }}.class)); + {{- end }} } @Test public void setPropertyRequest{{.Name}}() { - {{- if or .IsPrimitive (eq .KindType "enum") }} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - {{- else }} - {{javaReturn "" . }} newValue = {{javaDefault "" . }}; - {{- end }} + {{- template "prepareTestValue" .}} - testedClient.set{{Camel .Name}}(newValue); + testedClient.set{{Camel .Name}}(test{{ javaVar .}}); Robolectric.flushForegroundThreadScheduler(); inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); @@ -215,15 +232,12 @@ public class {{Camel .Interface.Name }}ClientTest assertEquals({{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(), response.what); Bundle data = response.getData(); - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - {{javaReturn "" . }} receivedByService = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else if (eq .KindType "bool")}} - {{javaReturn "" . }} receivedByService = = data.getInt("{{.Name}}", -1); - {{- else }} + {{- if not (.IsPrimitive) }} data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" . }} receivedByService = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); {{- end }} - assertEquals(receivedByService, newValue); + {{template "getReceivedFromBundle" . }} + {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} + assertEquals(received{{javaVar .}}, test{{ javaVar .}}); } {{- end}} @@ -235,25 +249,18 @@ public class {{Camel .Interface.Name }}ClientTest Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue()); Bundle data = new Bundle(); {{- range .Params }} - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); - {{- else if (eq .KindType "bool")}} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.putInt("{{.Name}}", {{javaVar .}}); - {{- else if eq .KindType "enum"}} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- else }} - {{javaReturn "" . }} {{javaVar . }} = {{javaDefault "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- end }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} {{- end }} msg.setData(data); mClientMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}({{javaVars .Params}}); + + inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}( + {{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}} + {{- if or (.IsPrimitive) (eq .KindType "enum") }}test{{javaVar $p}}{{- else }} any({{javaReturn "" . }}.class) {{- end -}}{{- end -}} + ); } {{- end}} @@ -266,30 +273,37 @@ public class {{Camel .Interface.Name }}ClientTest // Execute method {{- range .Params }} - {{- if or (and (.IsPrimitive) (not (eq .KindType "bool")) ) (eq .KindType "enum") }} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - {{- else if (eq .KindType "bool")}} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - {{- else }} - {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; - {{- end }} + {{- template "prepareTestValue" .}} {{- end }} + {{- if not .Return.IsVoid }} - {{- if or .Return.IsPrimitive (eq .Return.KindType "enum") }} + {{- if .Return.IsArray }} + {{javaElementType "" .Return}} elementForResult = {{javaTestValue "" .Return }}; + // todo fill if is struct + {{javaReturn "" .Return }} expectedResult = new {{javaReturn "" .Return }}{elementForResult} ; + {{- else if (.Return.IsPrimitive) }} + {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; + {{- else if eq .Return.KindType "enum"}} {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; {{- else }} - {{javaReturn "" .Return }} expectedResult = {{javaDefault "" .Return }}; + {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; + //TODO fill fields {{- end }} + {{- end }} AtomicBoolean receivedResp = new AtomicBoolean(false); - {{javaAsyncReturn "" .Return}} resFuture = testedClient.{{camel .Name}}Async({{javaVars .Params}}); + {{javaAsyncReturn "" .Return}} resFuture = testedClient.{{camel .Name}}Async({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}}test{{javaVar $p}}{{- end }}); resFuture.thenAccept(result -> { - {{- if and (.Return.IsPrimitive) (not (eq .Return.KindType "string")) }} + {{- if not .Return.IsVoid }} + {{- if .Return.IsArray }} + assertEquals(expectedResult, result); + {{- else if and (.Return.IsPrimitive) (not (eq .Return.KindType "string")) }} assertEquals(expectedResult, result.{{camel (javaType "" .Return)}}Value()); {{- else }} assertEquals(expectedResult, result); {{- end }} + {{- end }} receivedResp.set(true); }); Robolectric.flushForegroundThreadScheduler(); @@ -297,38 +311,38 @@ public class {{Camel .Interface.Name }}ClientTest // Expect msg to be sent. inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message method_request = messageCaptor.getValue(); assertEquals({{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Req.getValue(), method_request.what); - - Bundle req_data = method_request.getData(); - {{- range .Params }} - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - {{javaReturn "" . }} received{{javaVar .}} = req_data.get{{ ( Camel (javaType "" .) ) }}("result", -1); - {{- else if (eq .KindType "bool")}} - {{javaReturn "" . }} received{{javaVar .}} = = req_data.getInt("{{javaVar .}}", -1); - {{- else }} - req_data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" . }} received{{javaVar .}} = req_data.getParcelable("result", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); - {{- end }} - assertEquals(received{{javaVar .}}, {{javaVar .}}); + Bundle data = method_request.getData(); + {{- range .Params }} + {{- if not (.IsPrimitive) }} + data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + {{- end }} + {{- end }} + {{- range .Params }} + {{template "getReceivedFromBundle" . }} + assertEquals(received{{javaVar .}}, test{{javaVar .}}); {{- end }} - int returnedCallId = req_data.getInt("callId", -1); + int returnedCallId = data.getInt("callId", -1); //Prepare response Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue()); - Bundle data = new Bundle(); - data.putInt("callId", returnedCallId); - {{- if not .Return.IsVoid }} - {{- if .Return.IsPrimitive }} - data.put{{ ( Camel (javaType "" .Return) ) }}("result",expectedResult); - {{- else }} - data.putParcelable("result", new {{Camel .Return.Type}}Parcelable(expectedResult)); - {{- end }} - {{- end }} + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + {{- if not .Return.IsVoid }} + {{- if .Return.IsPrimitive }} + result_data.put{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result", expectedResult); + {{- else if .Return.IsArray }} + result_data.putParcelableArray("result", {{Camel (javaElementType "" .Return) }}Parcelable.wrapArray(expectedResult)); + {{- else }} + result_data.putParcelable("result", new {{Camel (javaElementType "" .Return) }}Parcelable(expectedResult)); + {{- end }} + {{- end }} - msg.setData(data); + msg.setData(result_data); method_request.replyTo.send(msg); Robolectric.flushForegroundThreadScheduler(); diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index 3195158..fd0b43a 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -187,19 +187,15 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service case UNREGISTER_CLIENT: removeClientActivity(msg.getData().getString("connectionID")); break; - //TODO ENUMS AND ARRAYS (for array just change the func to getXArray) {{- range .Interface.Properties }} case PROP_{{Camel .Name}}: { Bundle data = msg.getData(); - {{- if .IsPrimitive }} - {{javaReturn "" .}} newValue = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else }} + {{- if not (.IsPrimitive)}} data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" .}} newValue = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); - {{- end }} - - mBackendService.set{{Camel .Name}}(newValue); + {{- end}} + {{template "getDataFromBundle" . }} + mBackendService.set{{Camel .Name}}({{javaVar .}}); break; } {{- end }} @@ -218,33 +214,17 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service int callId = data.getInt("callId"); {{- range .Params }} - {{- if .IsPrimitive }} - {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else }} - {{javaReturn "" .}}Parcelable {{javaVar .}}Parcelable = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class); - {{javaReturn "" .}} {{javaVar .}} = {{javaVar .}}Parcelable.get{{Camel (javaReturn "" .)}}(); - {{- end }} + {{template "getDataFromBundle" . }} {{- end }} - {{- if .Return.IsPrimitive }} - {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} result = {{- end }}mBackendService.{{camel .Name}}({{javaVars .Params}}); - {{- else }} - {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} result = {{- end }} mBackendService.{{camel .Name}}({{javaVars .Params}}); - {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} - {{javaReturn "" .Return}}Parcelable result = new {{javaReturn "" .Return}}Parcelable(dataResult); - {{- end }} - {{- end }} + {{ if not .Return.IsVoid }}{{javaReturn "" .Return}} result = {{ end}} mBackendService.{{camel .Name}}({{javaVars .Params}}); Message respMsg = new Message(); respMsg.what = {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue(); Bundle resp_data = new Bundle(); resp_data.putInt("callId", callId); {{- if not .Return.IsVoid }} - {{- if (.Return.IsPrimitive) }} - resp_data.put{{ ( Camel (javaType "" .Return) ) }}("result", result); - {{- else }} - resp_data.putParcelable("result", result); - {{- end }} + {{ template "putResultIntoBundle" . }} {{- end }} respMsg.setData(resp_data); @@ -291,16 +271,9 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service Message msg = new Message(); msg.what = {{$InterfaceName}}MessageType.INIT.getValue(); Bundle data = new Bundle(); - //TODO ArrayProperties, enums {{range .Interface.Properties}} {{javaReturn "" .}} {{javaVar .}} = mBackendService.get{{Camel .Name}}(); - {{- if and (.IsPrimitive) (not (eq .KindType "bool")) }} - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); - {{- else if (eq .KindType "bool")}} - data.putInt("{{.Name}}", {{javaVar .}}); - {{- else }} - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- end }} + {{template "putDataIntoBundle" .}} {{- end}} msg.setData(data); sendMessageToClients(msg); @@ -308,17 +281,13 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service {{- range .Interface.Properties }} @Override - public void on{{Camel .Name}}Changed({{javaType "" .}} newValue){ - Log.i(TAG, "New value for {{Camel .Name}} from backend" + newValue); + public void on{{Camel .Name}}Changed({{javaType "" .}} {{javaVar .}}){ + Log.i(TAG, "New value for {{Camel .Name}} from backend" + {{javaVar .}}); Message msg = new Message(); msg.what = {{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue(); Bundle data = new Bundle(); - {{- if .IsPrimitive }} - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); - {{- else }} - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); - {{- end }} + {{template "putDataIntoBundle" . }} msg.setData(data); sendMessageToClients(msg); } @@ -332,11 +301,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service msg.what = {{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue(); Bundle data = new Bundle(); {{- range .Params }} - {{- if .IsPrimitive }} - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{ javaVar .}}); - {{- else }} - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- end }} + {{template "putDataIntoBundle" . }} {{- end }} msg.setData(data); sendMessageToClients(msg); diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 1cbcba2..9c52393 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -85,6 +85,21 @@ interface I{{Camel .Interface.Name }}MessageGetter } {{- $InterfaceName := Camel .Interface.Name}} +{{- define "prepareInitValue"}} + {{- if .IsArray }} + {{javaElementType "" .}} init_element{{ javaVar .}} = {{javaTestValue "" . }}; + // todo fill if is struct + {{javaReturn "" . }} init{{ javaVar .}} = new {{javaReturn "" . }}{ init_element{{ javaVar .}} } ; + {{- else if (.IsPrimitive) }} + {{javaReturn "" . }} init{{ javaVar .}} = {{javaTestValue "" . }}; + {{- else if eq .KindType "enum"}} + {{javaReturn "" . }} init{{ javaVar .}} = {{javaTestValue "" . }}; + {{- else }} + {{javaReturn "" . }} init{{ javaVar .}} = {{javaTestValue "" . }}; + //TODO fill fields + {{- end }} +{{- end }} + @Config(sdk = 33, manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class {{Camel .Interface.Name }}ServiceAdapterTest @@ -144,11 +159,8 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest void registerFakeActivityClient(Messenger messenger, String id) { {{- range .Interface.Properties}} - {{- if or .IsPrimitive (eq .KindType "enum" ) }} - when(backendServiceMock.get{{Camel .Name}}()).thenReturn({{javaTestValue "" .}}); - {{- else }} - when(backendServiceMock.get{{Camel .Name}}()).thenReturn({{javaDefault "" .}}); - {{- end }} + {{- template "prepareInitValue" .}} + when(backendServiceMock.get{{Camel .Name}}()).thenReturn(init{{javaVar .}}); {{- end }} @@ -172,22 +184,18 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest assertEquals({{$InterfaceName}}MessageType.INIT.getValue(), response.what); Bundle data = response.getData(); + {{- range .Interface.Properties}} + {{ template "getReceivedFromBundle" .}} + {{- end }} {{- range .Interface.Properties}} {{- if not .IsPrimitive }} data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); {{- end }} {{- end }} {{- range .Interface.Properties}} - assertTrue(data.containsKey("{{.Name}}")); - {{- if .IsPrimitive }} - assertEquals(data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1), {{javaTestValue "" .}}); - {{- else if eq .KindType "enum" }} - // assertEquals(data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(), {{javaTestValue "" .}}); - {{- else }} - // TODO uncomment after adding comparision operator - // assertEquals(data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(), {{javaDefault "" .}}); + {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} + assertEquals(received{{javaVar .}}, init{{ javaVar .}}); {{- end }} - {{- end }} } @@ -234,35 +242,22 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue()); Bundle data = new Bundle(); - {{- if .IsPrimitive }} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", newValue); - data.putInt("{{.Name}}", newValue); - {{- else if (eq .KindType "enum") }} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); - {{- else }} - {{javaReturn "" . }} newValue = {{javaDefault "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable(newValue)); - {{- end }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} msg.setData(data); mServiceMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - inOrderBackendService.verify(backendServiceMock,times(1)).set{{Camel .Name}}(newValue); + inOrderBackendService.verify(backendServiceMock,times(1)).set{{Camel .Name}}({{- if or (.IsPrimitive) (eq .KindType "enum") }}test{{javaVar .}}{{- else }} any({{javaReturn "" . }}.class) {{- end -}}); } @Test public void whenNotified{{.Name}}() { - {{- if or .IsPrimitive (eq .KindType "enum" ) }} - {{javaReturn "" . }} newValue = {{javaTestValue "" . }}; - {{- else }} - {{javaReturn "" . }} newValue = {{javaDefault "" . }}; - {{- end }} + {{- template "prepareTestValue" .}} - testedAdapterAsEventListener.on{{Camel .Name}}Changed(newValue); + testedAdapterAsEventListener.on{{Camel .Name}}Changed(test{{javaVar .}}); Robolectric.flushForegroundThreadScheduler(); inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); @@ -270,13 +265,11 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest assertEquals({{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue(), response.what); Bundle data = response.getData(); - {{- if .IsPrimitive }} - {{javaReturn "" . }} receivedByClient = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" . }} receivedByClient = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); - {{- end }} - assertEquals(receivedByClient, newValue); + + {{template "getReceivedFromBundle" . }} + {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} + assertEquals(received{{javaVar .}}, test{{javaVar .}}); + } {{- end}} @@ -285,14 +278,10 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest public void whenNotified{{.Name}}() { {{- range .Params }} - {{- if or .IsPrimitive (eq .KindType "enum" ) }} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - {{- else }} - {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; - {{- end }} + {{- template "prepareTestValue" .}} {{- end }} - testedAdapterAsEventListener.on{{Camel .Name}}({{javaVars .Params}}); + testedAdapterAsEventListener.on{{Camel .Name}}({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}}test{{javaVar $p}}{{- end }}); Robolectric.flushForegroundThreadScheduler(); inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); @@ -306,12 +295,9 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- end }} {{- end }} {{- range .Params }} - {{- if .IsPrimitive }} - {{javaReturn "" . }} receivedByClient{{javaVar .}} = data.get{{ ( Camel (javaType "" .) ) }}("{{.Name}}", -1); - {{- else }} - {{javaReturn "" . }} receivedByClient{{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); - {{- end }} - assertEquals(receivedByClient{{javaVar .}}, {{javaVar .}}); + {{template "getReceivedFromBundle" . }} + {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} + assertEquals(received{{javaVar .}}, test{{ javaVar .}}); {{- end}} } {{- end}} @@ -324,32 +310,39 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest // Create and send message Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Req.getValue()); Bundle data = new Bundle(); - {{- range .Params }} - {{- if .IsPrimitive }} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.put{{ ( Camel (javaType "" .) ) }}("{{.Name}}", {{javaVar .}}); - {{- else if eq .KindType "enum" }} - {{javaReturn "" . }} {{javaVar .}} = {{javaTestValue "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- else }} - {{javaReturn "" . }} {{javaVar .}} = {{javaDefault "" . }}; - data.putParcelable("{{.Name}}", new {{Camel .Type}}Parcelable({{javaVar .}})); - {{- end }} - {{- end }} + int callId = 99; + data.putInt("callId", callId); + {{- range .Params }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} + {{- end}} {{- if not .Return.IsVoid }} - {{- if and .Return.IsPrimitive}} + {{- if .Return.IsArray }} + {{javaElementType "" .Return}} elementForResult = {{javaTestValue "" .Return }}; + // todo fill if is struct + {{javaReturn "" .Return }} returnedValue = new {{javaReturn "" .Return }}{elementForResult} ; + {{- else if (.Return.IsPrimitive) }} + {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; + {{- else if eq .Return.KindType "enum"}} {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; {{- else }} - {{javaReturn "" .Return }} returnedValue = {{javaDefault "" .Return }}; + {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; + //TODO fill fields {{- end }} - when(backendServiceMock.{{camel .Name}}({{javaVars .Params}})).thenReturn(returnedValue); + + + when(backendServiceMock.{{camel .Name}}({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}} + {{- if or (.IsPrimitive) (eq .KindType "enum") }}test{{javaVar $p}}{{- else }} any({{javaReturn "" . }}.class) {{- end -}}{{- end -}} + )).thenReturn(returnedValue); {{- end}} msg.setData(data); mServiceMessenger.send(msg); Robolectric.flushForegroundThreadScheduler(); - inOrderBackendService.verify(backendServiceMock,times(1)).{{camel .Name}}({{javaVars .Params}}); + inOrderBackendService.verify(backendServiceMock,times(1)).{{camel .Name}}({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}} + {{- if or (.IsPrimitive) (eq .KindType "enum") }}test{{javaVar $p}}{{- else }} any({{javaReturn "" . }}.class) {{- end -}}{{- end -}} + ); //Now verify it was sent back to caller Robolectric.flushForegroundThreadScheduler(); @@ -358,16 +351,21 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest assertEquals({{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue(), response.what); Bundle resp_data = response.getData(); + {{- if not .Return.IsVoid }} {{- if .Return.IsPrimitive }} - {{javaReturn "" .Return }} receivedByClient = resp_data.get{{ ( Camel (javaType "" .Return) ) }}("result", -1); - {{- else }} + {{javaReturn "" .Return }} receivedByClient = resp_data.get{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result"{{if not .Return.IsArray}}, {{javaDefault "" .Return}}{{end}}); + {{- else if .Return.IsArray }} + resp_data.setClassLoader({{Camel .Return.Type}}Parcelable.class.getClassLoader()); + {{javaReturn "" .Return }} receivedByClient = {{Camel .Return.Type}}Parcelable.unwrapArray(({{Camel .Return.Type}}Parcelable[])resp_data.getParcelableArray("result", {{Camel .Return.Type}}Parcelable.class)); + {{- else }} resp_data.setClassLoader({{Camel .Return.Type}}Parcelable.class.getClassLoader()); {{javaReturn "" .Return }} receivedByClient = resp_data.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); {{- end }} - assertEquals(receivedByClient, returnedValue); {{- end}} + resp_data.getInt("callId", 0); + assertEquals(receivedByClientId, callId); } {{- end}} From a7bfb0e71417a09f4f390bd0a13bfbd89824e927 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 31 Jul 2025 12:17:44 +0200 Subject: [PATCH 24/45] feat(example): add test service example skeleton service uses a stub as the backend. It is generated only for the first api interface. User needs to fill the logic of changing properties and emitting singals. All the buttons and text views are already correctly connected. It provides a buttons to start or stop service, change properties, emit singals. The methods are also working, if the client sends a request it is served, but the logic needs to be implemented in the stub i.e the _impl module. This is part of adding the android-jni feature. --- rules.yaml | 117 +++++++-- templates/settings.gradle.tpl | 3 + .../res/drawable/ic_launcher_background.xml | 170 +++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 +++ .../res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../testappres/res/values-night/themes.xml | 16 ++ templates/testappres/res/values/colors.xml | 10 + .../res/values/stringsclient.xml.tpl | 3 + .../res/values/stringsservice.xml.tpl | 3 + templates/testappres/res/values/themes.xml | 16 ++ templates/testappres/res/xml/backup_rules.xml | 13 + .../res/xml/data_extraction_rules.xml | 19 ++ templates/testclientapp/application.java.tpl | 4 +- .../testserviceapp/AndroidManifest.xml.tpl | 33 +++ templates/testserviceapp/application.java.tpl | 224 ++++++++++++++++++ templates/testserviceapp/build.gradle.tpl | 42 ++++ templates/testserviceapp/proguard-rules.pro | 21 ++ 28 files changed, 714 insertions(+), 22 deletions(-) create mode 100644 templates/testappres/res/drawable/ic_launcher_background.xml create mode 100644 templates/testappres/res/drawable/ic_launcher_foreground.xml create mode 100644 templates/testappres/res/mipmap-anydpi/ic_launcher.xml create mode 100644 templates/testappres/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 templates/testappres/res/mipmap-hdpi/ic_launcher.webp create mode 100644 templates/testappres/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 templates/testappres/res/mipmap-mdpi/ic_launcher.webp create mode 100644 templates/testappres/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 templates/testappres/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 templates/testappres/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 templates/testappres/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 templates/testappres/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 templates/testappres/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 templates/testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 templates/testappres/res/values-night/themes.xml create mode 100644 templates/testappres/res/values/colors.xml create mode 100644 templates/testappres/res/values/stringsclient.xml.tpl create mode 100644 templates/testappres/res/values/stringsservice.xml.tpl create mode 100644 templates/testappres/res/values/themes.xml create mode 100644 templates/testappres/res/xml/backup_rules.xml create mode 100644 templates/testappres/res/xml/data_extraction_rules.xml create mode 100644 templates/testserviceapp/AndroidManifest.xml.tpl create mode 100644 templates/testserviceapp/application.java.tpl create mode 100644 templates/testserviceapp/build.gradle.tpl create mode 100644 templates/testserviceapp/proguard-rules.pro diff --git a/rules.yaml b/rules.yaml index ad98e50..b808420 100644 --- a/rules.yaml +++ b/rules.yaml @@ -152,62 +152,139 @@ features: target: "AndroidManifest.xml" - source: "testclientapp/application.java.tpl" target: "java/{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/{{Camel .Module.Name}}TestClientApp.java" - - source: "testclientapp/res/drawable/ic_launcher_background.xml" + - source: "testappres/res/drawable/ic_launcher_background.xml" target: "res/drawable/ic_launcher_background.xml" raw: true - - source: "testclientapp/res/drawable/ic_launcher_foreground.xml" + - source: "testappres/res/drawable/ic_launcher_foreground.xml" target: "res/drawable/ic_launcher_foreground.xml" raw: true - - source: "testclientapp/res/values/colors.xml" + - source: "testappres/res/values/colors.xml" target: "res/values/colors.xml" raw: true - - source: "testclientapp/res/values/strings.xml.tpl" + - source: "testappres/res/values/stringsclient.xml.tpl" target: "res/values/strings.xml" - - source: "testclientapp/res/values/themes.xml" + - source: "testappres/res/values/themes.xml" target: "res/values/themes.xml" raw: true - - source: "testclientapp/res/values-night/themes.xml" + - source: "testappres/res/values-night/themes.xml" target: "res/values-night/themes.xml" raw: true - - source: "testclientapp/res/xml/backup_rules.xml" + - source: "testappres/res/xml/backup_rules.xml" target: "res/xml/backup_rules.xml" raw: true - - source: "testclientapp/res/xml/data_extraction_rules.xml" + - source: "testappres/res/xml/data_extraction_rules.xml" target: "res/xml/data_extraction_rules.xml" raw: true - - source: "testclientapp/res/mipmap-anydpi/ic_launcher.xml" + - source: "testappres/res/mipmap-anydpi/ic_launcher.xml" target: "res/mipmap-anydpi/ic_launcher.xml" raw: true - - source: "testclientapp/res/mipmap-anydpi/ic_launcher_round.xml" + - source: "testappres/res/mipmap-anydpi/ic_launcher_round.xml" target: "res/mipmap-anydpi/ic_launcher_round.xml" raw: true - - source: "testclientapp/res/mipmap-hdpi/ic_launcher.webp" + - source: "testappres/res/mipmap-hdpi/ic_launcher.webp" target: "res/mipmap-hdpi/ic_launcher.webp" raw: true - - source: "testclientapp/res/mipmap-hdpi/ic_launcher_round.webp" + - source: "testappres/res/mipmap-hdpi/ic_launcher_round.webp" target: "res/mipmap-hdpi/ic_launcher_round.webp" raw: true - - source: "testclientapp/res/mipmap-mdpi/ic_launcher.webp" + - source: "testappres/res/mipmap-mdpi/ic_launcher.webp" target: "res/mipmap-mdpi/ic_launcher.webp" raw: true - - source: "testclientapp/res/mipmap-mdpi/ic_launcher_round.webp" + - source: "testappres/res/mipmap-mdpi/ic_launcher_round.webp" target: "res/mipmap-mdpi/ic_launcher_round.webp" raw: true - - source: "testclientapp/res/mipmap-xhdpi/ic_launcher.webp" + - source: "testappres/res/mipmap-xhdpi/ic_launcher.webp" target: "res/mipmap-xhdpi/ic_launcher.webp" raw: true - - source: "testclientapp/res/mipmap-xhdpi/ic_launcher_round.webp" + - source: "testappres/res/mipmap-xhdpi/ic_launcher_round.webp" target: "res/mipmap-xhdpi/ic_launcher_round.webp" raw: true - - source: "testclientapp/res/mipmap-xxhdpi/ic_launcher.webp" + - source: "testappres/res/mipmap-xxhdpi/ic_launcher.webp" target: "res/mipmap-xxhdpi/ic_launcher.webp" raw: true - - source: "testclientapp/res/mipmap-xxhdpi/ic_launcher_round.webp" + - source: "testappres/res/mipmap-xxhdpi/ic_launcher_round.webp" target: "res/mipmap-xxhdpi/ic_launcher_round.webp" raw: true - - source: "testclientapp/res/mipmap-xxxhdpi/ic_launcher.webp" + - source: "testappres/res/mipmap-xxxhdpi/ic_launcher.webp" target: "res/mipmap-xxxhdpi/ic_launcher.webp" raw: true - - source: "testclientapp/res/mipmap-xxxhdpi/ic_launcher_round.webp" + - source: "testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp" + target: "res/mipmap-xxxhdpi/ic_launcher_round.webp" + raw: true + + - name: testserviceapp + requires: + - api + - android + scopes: + - match: module + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}serviceexample/" + documents: + - source: "testserviceapp/build.gradle.tpl" + target: "build.gradle" + - match: module + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}serviceexample/src/main/" + documents: + - source: "testserviceapp/AndroidManifest.xml.tpl" + target: "AndroidManifest.xml" + - source: "testserviceapp/application.java.tpl" + target: "java/{{dot .Module.Name}}/{{dot .Module.Name}}serviceexample/{{Camel .Module.Name}}TestServiceApp.java" + - source: "testappres/res/drawable/ic_launcher_background.xml" + target: "res/drawable/ic_launcher_background.xml" + raw: true + - source: "testappres/res/drawable/ic_launcher_foreground.xml" + target: "res/drawable/ic_launcher_foreground.xml" + raw: true + - source: "testappres/res/values/colors.xml" + target: "res/values/colors.xml" + raw: true + - source: "testappres/res/values/stringsservice.xml.tpl" + target: "res/values/strings.xml" + - source: "testappres/res/values/themes.xml" + target: "res/values/themes.xml" + raw: true + - source: "testappres/res/values-night/themes.xml" + target: "res/values-night/themes.xml" + raw: true + - source: "testappres/res/xml/backup_rules.xml" + target: "res/xml/backup_rules.xml" + raw: true + - source: "testappres/res/xml/data_extraction_rules.xml" + target: "res/xml/data_extraction_rules.xml" + raw: true + - source: "testappres/res/mipmap-anydpi/ic_launcher.xml" + target: "res/mipmap-anydpi/ic_launcher.xml" + raw: true + - source: "testappres/res/mipmap-anydpi/ic_launcher_round.xml" + target: "res/mipmap-anydpi/ic_launcher_round.xml" + raw: true + - source: "testappres/res/mipmap-hdpi/ic_launcher.webp" + target: "res/mipmap-hdpi/ic_launcher.webp" + raw: true + - source: "testappres/res/mipmap-hdpi/ic_launcher_round.webp" + target: "res/mipmap-hdpi/ic_launcher_round.webp" + raw: true + - source: "testappres/res/mipmap-mdpi/ic_launcher.webp" + target: "res/mipmap-mdpi/ic_launcher.webp" + raw: true + - source: "testappres/res/mipmap-mdpi/ic_launcher_round.webp" + target: "res/mipmap-mdpi/ic_launcher_round.webp" + raw: true + - source: "testappres/res/mipmap-xhdpi/ic_launcher.webp" + target: "res/mipmap-xhdpi/ic_launcher.webp" + raw: true + - source: "testappres/res/mipmap-xhdpi/ic_launcher_round.webp" + target: "res/mipmap-xhdpi/ic_launcher_round.webp" + raw: true + - source: "testappres/res/mipmap-xxhdpi/ic_launcher.webp" + target: "res/mipmap-xxhdpi/ic_launcher.webp" + raw: true + - source: "testappres/res/mipmap-xxhdpi/ic_launcher_round.webp" + target: "res/mipmap-xxhdpi/ic_launcher_round.webp" + raw: true + - source: "testappres/res/mipmap-xxxhdpi/ic_launcher.webp" + target: "res/mipmap-xxxhdpi/ic_launcher.webp" + raw: true + - source: "testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp" target: "res/mipmap-xxxhdpi/ic_launcher_round.webp" raw: true diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index 0dc106f..b9e32e5 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -34,3 +34,6 @@ include ':{{camel .Module.Name}}_api' {{- if .Features.testclientapp }} include ':{{dot .Module.Name}}_client_example' {{- end -}} +{{- if .Features.testserviceapp }} +include ':{{dot .Module.Name}}serviceexample' +{{- end -}} diff --git a/templates/testappres/res/drawable/ic_launcher_background.xml b/templates/testappres/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/templates/testappres/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/testappres/res/drawable/ic_launcher_foreground.xml b/templates/testappres/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/templates/testappres/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/templates/testappres/res/mipmap-anydpi/ic_launcher.xml b/templates/testappres/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/templates/testappres/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/testappres/res/mipmap-anydpi/ic_launcher_round.xml b/templates/testappres/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/templates/testappres/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/testappres/res/mipmap-hdpi/ic_launcher.webp b/templates/testappres/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/templates/testappres/res/mipmap-hdpi/ic_launcher_round.webp b/templates/testappres/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/templates/testappres/res/mipmap-mdpi/ic_launcher.webp b/templates/testappres/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/templates/testappres/res/mipmap-xhdpi/ic_launcher.webp b/templates/testappres/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/templates/testappres/res/mipmap-xhdpi/ic_launcher_round.webp b/templates/testappres/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 zcmV-U53%r4Nk&FS4*&pHMM6+kP&il$0000G0001A003VA06|PpNSy@$00HoY|G(*G z+qV7x14$dSO^Re!iqt-AAIE9iwr$(CZQJL$blA4B`>;C3fBY6Q8_YSjb2%a=fc}4E zrSzssacq<^nmW|Rs93PJni30R<8w<(bK_$LO4L?!_OxLl$}K$MUEllnMK|rg=f3;y z*?;3j|Nh>)p0JQ3A~rf(MibH2r+)3cyV1qF&;8m{w-S*y+0mM){KTK^M5}ksc`qX3 zy>rf^b>~l>SSHds8(I@hz3&PD@LmEs4&prkT=BjsBCXTMhN$_)+kvnl0bLKW5rEsj z*d#KXGDB4P&>etx0X+`R19yC=LS)j!mgs5M0L~+o-T~Jl!p!AJxnGAhV%~rhYUL4hlWhgES3Kb5oA&X z{}?3OBSS-{!v$nCIGj->(-TAG)8LR{htr41^gxsT8yqt2@DEG6Yl`Uma3Nd4;YUoW zTbkYl3CMU5ypMF3EIkYmWL|*BknM`0+Kq6CpvO(y$#j94e+q{vI{Zp8cV_6RK!`&C zob$*5Q|$IZ09dW=L!V zw@#2wviu|<#3lgGE8GEhcx+zBt`} zOwP8j9X%^f7i_bth4PiJ$LYtFJSCN$3xwDN;8mr*B;CJwBP2G0TMq0uNt7S^DO_wE zepk!Wrn#Z#03j{`c*Rf~y3o7?J}w?tEELRUR2cgxB*Y{LzA#pxHgf}q?u5idu>077 zd^=p)`nA}6e`|@`p?u}YU66PP_MA}Zqqe!c{nK&z%Jwq1N4e_q<#4g^xaz=ao;u|6 zwpRcW2Lax=ZGbx=Q*HhlJ`Ns#Y*r0*%!T?P*TTiX;rb)$CGLz=rSUum$)3Qyv{BL2 zO*=OI2|%(Yz~`pNEOnLp>+?T@glq-DujlIp?hdJeZ7ctP4_OKx|5@EOps3rr(pWzg zK4d3&oN-X2qN(d_MkfwB4I)_)!I_6nj2iA9u^pQ{;GckGLxBGrJUM2Wdda!k)Y>lq zmjws>dVQ*vW9lvEMkiN3wE-__6OWD0txS&Qn0n22cyj4Q*8(nG4!G{6OOwNvsrPIL zCl-$W9UwkEUVuLwyD%|inbOF*xMODZ4VMEVAq_zUxZ+K#Gdqf!DW$5f)?7UNOFMz! zrB~tuu=6X2FE(p^iqgxr+?ZK;=yz`e;C$#_@D9Lj-+TDVOrva>(#*PVbaHO>A)mhl z07OJWCqYC60518$!&c`eNBcBW%GnfaQ*$eazV^2_AW?j)h;J1nUjN(I9=0+!RVx~% z3@Tf!P0TE+98jA?WceK-}A1% zW!K)lyKcGqy#M~})315-A#2NXQ`?6NR#Apo=S!oF=JfpX>iR*49ec{7AN$xxpK{D$ z2d%Fz&rdfSqourN$~Y^NFIMV1CZ?J*bMx~H3k&meGtH@q9ra2vZxmA$S(#jaaj-g4 ztJmxG+DLV<*q<|sDXPp$X>E)#S}Vm&sRaO5P&goh2><}FEdZSXDqsL$06sAkh(e+v zAsBhKSRexgwg6tIy~GFJzaTxXD(}|+0eOwFDA%rn`X;MVwDHT9=4=g%OaJ9s%3b9>9EUTnnp0t;2Zpa{*>mk~hZqItE_!dQ zOtC>8`$l|mV43Jbudf0N6&&X;{=z}Zi}d1`2qmJ}i|0*GsulD3>GgQXHN)pkR6sf1 z?5ZU%&xtL}oH;YiAA)d*^Ndw2T$+Mjuzyzz@-SM`9df7LqTxLuIwC~S0092~+=qYv z@*ja;?Wt!T!{U?c*Z0YtGe)XbI&y-?B&G2$`JDM)(dIV9G`Sc#6?sI60de6kv+)Qb zUW~2|WjvJq3TA8`0+sWA3zRhY9a~ow)O~&StBkG2{*{TGiY~S8ep{V&Vo2l<6LWsu z^#p0-v*t2?3&aA1)ozu|%efSR=XnpX$lvTeRdKlvM!@|pM5p2w3u-6 zU>}t2xiYLS+{|%C65AzX+23Mtlq?BS&YdYcYsVjoiE&rT>;Necn6l^K)T^lmE`5u{ zm1i+-a-gc;Z&v-{;8r)z6NYfBUv+=_L}ef}qa9FX01)+Aaf+;xj(mL6|JUzGJR1|fnanb%?BPPIp>SCjP|8qE5qJ{=n5ZGw?81z3(k;pzH%1CtlX50{E7h)$h{qGKfzC`e2o`*IqA#tjA z`Fz&^%$b9F*N`)U-#6>a)Z`55`$Dd0cfcs0$d13^ONrdCu9xcv_=n#WQo8stcz3jP9|2EvdI-RhJM3%Q%oM&!OlShM|0 z?gz?wHZSnm45njLtsz8PVT1S&jAlbKg5kVam$p16=EK@Sj4EP0OtH zmJDmdc^v)x>56Qg_wmYHz6h)>kl_h$>0@J!ypv%APmjZTAQVLy6Fu50RGY&JAVNhx zrF_qG6`x9MkT;1SFWo$)l{M$;3qUDn9JwE}z zRl#E_bDRJFii61kPgBybIgp8dNW!Cc1b*^YYk-#oWLJvtM_v^hQx~9?8LD4VFFxBF z3MlrsSC%f9Oupn*ctPL0U1fwfX?`tRhPD{PSLFPQOmIt$mDy0SgpNVvHS+f#Do>h1Gn?LZU9(KaN>Q_=Y*_T zvtD7%_u^^+{g`0VGzg(VZrpVQ6Ub5M=tI_p7T93R8@3Zulu3|#{iNcu!oiHxZ4Rf*( zfmiN$$ru(*_Zqn=`Gq#OuHRTSwp7uH_SokR&|)RuW5yo=Z|_4?qU-JU+tpt>!B&Is z@N(=SG;bpVc;AO@zbmMM zScqq1)b-ZQIrs={oD}|?6y{$HNB1U0^LsBh8JI&3!GBZxOXI<}&5-$lgkAaYqhOTb z?2vEnZ$-kk;*M_17(upJF3%+iH*s0-r{vttXVB2OUwI1s^+G(Ft(U8gYFXC}#P&E^ z>T@C^tS`Z7{6HT4_nF~n>JlZtk5&qDBl6r|^kzQYe`wq!C)n@$c>WOPA61NDFj<<6 zGW71NMMhwAl!U-yqrq2xrSFqRCI8acw7?}3j;ynxo*-b7Co;g5r%^j=H@9({PXXBf z@r>U>>N;E)81wx`B4f%{PB~MHka_);%kBCb(d|Jy5!MqJ%2p`t&@L)4$T2j&-WHvG zv3(uyA_gwqNu(k?jQTtv3dgPKRZoH8prxe7>pQBW5L&dpumS&5Ld2?(sCpJjvc4L5 zEnh&?91WVm)ZdTj=fjJ$pPDdgAttLXuke+?KdKxu*;kTC(r!tQk6;gxj4h%FdHAt(^M3YvYj(!tOeN)+Hvj6+< zzyJRG?^lZfWuR#t!tUKP&(?%3v&Zd$R2YN>lB(Lq`OInY48%4%yTv2 zYe1{G`3)(PDEio5Y@-I5tUf`c%%OCJMtSW56g3iEg%3`$7XSJJHyA z<|7&N)5Xrlgv~%BO24eFd;Hd;uiK%D`EdK|quUeRZDqbh9l)%j%J#0lfrZumvA<_w zu&=AVvdChf6}eqh(bUz`(`Ue*p01{fBAcTgKyDYLs_I+YyJEk+rM@avU~>fB$n)HS zM7pfJydu`i%gfS<{PF94kZDv$t>06sAkheDzu40NJ$5CMW%n^Lls?8^p^QGWURbKu3ZduZQZ((s2? zzE`}<{;Zt7<$C|9R8A~DJ~@%x>TfP zF>TX8)@v|t)q4GjRt<}5s6hLHwRel7>V@&r-O|Av(yh;Q1A{E>Ir>p+%dHD|=l+lT zpr(Dg&>#Nu=!)6bCLr-ZS%|;h)Ij$+e@r8_{qO19QvDe=&1tmpY*0lcA^Cc-#{9fQ z<~$*<&P$Q<_jy#<$40PMofM7aQ}C=jphI`4kLg}Z7CIN#26D{-4v-_CA-LiE@(%{y!BzsU%gG`Q?sjLUf%qFSl0y)2#ae*+EI>s|i`d^V$Dn)qmzqRq6VJRY|{4ujsIU%#bnqU6MR&-1I_43=|5(6Jr;Jvert) zE?S|Tmn}Tv<-??sxV5@9t}3D=>YZ0JrQe$CO~|EY=Lj9RM&4svQHPQL6%pV5fPFiH zfXDx;l@~et{*{U*#c#Dvzu)|znDO7$#CRx)Z&yp-}SrD{&|(MQtfUz~n35@RLfUy=aqrhCX0M}J_r5QsK~NmRCR|Nm&L z41UdsLjWxSUlL41r^0K&nCCK>fdR-!MYjFg(z9_mF^C|#ZQw?`)f6uVzF^`bRnVY& zo}@M06J&_+>w9@jpaO4snmU;0t-(zYW1qVBHtuD!d?%?AtN7Plp><-1Y8Rqb20ZaP zTCgn*-Sri4Q8Xn>=gNaWQ57%!D35UkA@ksOlPB*Dvw}t02ENAqw|kFhn%ZyyW%+t{ zNdM!uqEM^;2}f+tECHbwLmH*!nZVrb$-az%t50Y2pg(HqhvY-^-lb}>^6l{$jOI6} zo_kBzj%8aX|6H5M0Y<)7pzz_wLkIpRm!;PzY)9+24wk2&TT{w--phDGDCOz{cN_ca zpnm7`$oDy=HX%0i-`769*0M6(e5j-?(?24%)<)&46y0e&6@HCDZAm9W6Ib#Y#BF6- z=30crHGg+RRTe%VBC>T00OV6F+gQDAK38Ne3N9bm|62tPccBJi)5{B z4zc^Db72XiBd}v$CF|yU{Z=M|DZ%-(XarYNclODlb1Kz1_EKLy(NSLCN`eUl(rBCL zT*jx@wNvze0|TSqgE(QArOZU)_?qH(sj#TwzElLs9q)(0u!_P|R%Cy_0JFQxgGV>1 zz4?_uq<8_gM0`c*Hh|;UMz~vrg1gQXp{ufg`hM_qU;U>+zmvc5blCLSq@PrEBSGR# z&8=2Z4uXN`F3p73ueD1l{s{k$WipAvSh5W7ABe?4)t;r@V?y`bNB5FvBuE|0VRTb< zM1Hn^?DSsJY+sX@T5xW=#>T9VEV|?<(=6|ge$X6Sb05!LFdjDcoq*gM(Zq=t;_)Le&jyt(&9jzR73noru`a# zN*<`KwGa^gZU3-)MSLF0aFag#f0<>E(bYTeHmtdbns#|I)-$)mJ`q9ctQ8g0=ET?| zdO}eZ*b_p>ygRTtR^5Ggdam=Zb5wmd{}np+Jn1d_=M`~P=M67jj})fH4ztb5yQqQW z^C|C&^LHAK-u+ooIK)yM)QM?t;|<{P;;{`p=BclzAN#JzL4jCwXkQB1Dy{=^KR`=~ zTrr)y7eiYBzSNs_DvO=4A6#EgGS-zY%Vi)N*Yb`U;6o}KR}dq{r9pT5wqZ@3NOE8- z9-(}D|Nc5732CSYQbL)!gPQ#RbD8BhK3dl{sUuPvei0tkvnJBxDEAYTesU8H$)g(Plra{VH(v3u^CO1~(+ zU0O7#)jaS4{NcwA+LuSm&VBcX2#Im3xg)W}ySNw%->orn1taZ&+d)}8gJTqA!u|5P z{yv?zol_3|(1(%M(EVU=cp?L`{Pi|ixk{U)*guFML3P!OSlz;zGA#T+E@8@cgQ_mv1o7RSU=Zo_82F?&&2r;WE z@wk}JHYEZ9nYUc(Vv~iTCa3u8e4q(yq<29VoNbKk|`mq%I6u)My=gPIDuUb&lzf4`MEA9^g8u z)vp8|$$HE9m_BTV?lOosIGa4jud=jIbw)O2eCMfyw2*S8?hjWw^nqws$O*M$3I1)x zR0PWFb3$ySOcGTe1dz%N0l;RPc`x%05FtT^f^j{YCP}*Q=lvp4$ZXrTZQHhO+w%wJn3c8j%+5C3UAFD&%8dBl_qi9D5g8fry}6Ev z2_Q~)5^N$!IU`BPh1O|=BxQ#*C5*}`lluC515$lxc-vNC)IgW=K|=z7o%cWFpndn= zX}f{`!VK02_kU+Q5a3m37J;c} zTzbxteE{GNf?yLt5X=Bzc-mio^Up0nunMCgp*ZJ;%MJvPM3QK)BryP(_v@ei4UvHr z6+sbCifQaOkL6-;5fL8$W($zZ_;CZp305C;~$hhRquZr-r)jjd1z z31%ZK{-(`P#|Um_Sivn@p$-vz46uqT>QG0B1w9znfS9A8PB2LaHdzA|_)yjXVR*l{ zkcu3@vEf7bxH0nkh`q?8FmoO_Ucui*>_a~P?qQrlZ9@+D7%MTpSnztpylXrt5!-k8_QPB?YL8Kx_On8WD zgT+111d(Op$^$&KLAN5+@?>f7F4~wFi(8TL8+szgVmcMDTp5l&k6~=rA{Dt}!gb^r zSWY<)M7D|Z2P0cEodj6E42PV>&>DFmQpgt)E-|#sSUU@uKed+F680H@<;-x{p|nuH4!_mn85rx>wz;0mPi2ZkL#k6;sznu?cXh!T0S>{w6 zL^gvR05NY64l*<+_L>On$rjx9!US;l;LX6@z}yi#2XHh)F@Oo+l)h%fq$v}DNmF2> zfs^_t0)3N-W<9-N?uedVv{)-J0W5mh#29QM5R5h&KuiRM=0Zvnf#lF=K#WlCgc#9c zS;qvh(P$!_a8JwyhI^ZJV2k+B6Z^64?w|1?5gyo6y{}923CRZfYVe1#?F% z7h2SUiNO3;T#JUOyovSs@@C1GtwipycA=*x5{BpIZ_#GCMuV8XK=x;qCNy{d7?wA~ zC+=vjls;ci&zW=6$H~4^K%v{p}Ab?U%C6Z4p%eC<3ExqU$XR<}LLF67A$Sr20DR_pJ3yeBa~ z^sw{V0FI5;UpwXsScYuhbqGQ`YQ25;6p6W^+tgL&;Ml;>S3CGpSZ>VrTn0m1$y$HU z&65)I!c?oREz};c=nLCliriqQX->4uivHTgd${GqeAlf*!P^B|jkU|*IdNP(&6C>4 zqOW$)Nw9nvjy^&`?E|gotDV{JmJ9Q~vuhy<`^C4XIUDt|j4o6rK^e8_(=YqC zuaR6TRVf@tUFHB079o4MBIh{M~4>WwnGgesQH*3?w(RA%hCZ*7)b!aNV=yOQ%o_Y=Lt0Sl*(9^jfRnC210Om$=y>*o|3z} zAR&vAdrB#mWoaB0fJSw9xw|Am$fzK>rx-~R#7IFSAwdu_EI|SRfB*yl0w8oX09H^q zAjl2?0I)v*odGJ40FVGaF&2qJq9Gv`>V>2r0|c`GX8h>CX8eHcOy>S0@<;M3<_6UM z7yCEpug5NZL!H_0>Hg_HasQGxR`rY&Z{geOy?N92Z z{lER^um|$*?*G63*njwc(R?NT)Bei*3jVzR>FWUDb^gKhtL4A=kE_1p-%Fo2`!8M} z(0AjuCiS;G{?*^1tB-uY%=)SRx&D)pK4u@>f6@KPe3}2j_har$>HqzH;UCR^ssFD0 z7h+VLO4o@_Yt>>AeaZKUxqyvxWCAjKB>qjQ30UA)#w z&=RmdwlT`7a8J8Yae=7*c8XL|{@%wA8uvCqfsNX^?UZsS>wX}QD{K}ad4y~iO*p%4 z_cS{u7Ek%?WV6em2(U9#d8(&JDirb^u~7wK4+xP$iiI6IlD|a&S)6o=kG;59N|>K1 zn(0mUqbG3YIY7dQd+*4~)`!S9m7H6HP6YcKHhBc#b%1L}VIisp%;TckEkcu0>lo@u995$<*Em;XNodjTiCdC%R+TX|_ZR#|1`RR|`^@Teh zl#w@8fI1FTx2Dy+{blUT{`^kY*V-AZUd?ZZqCS4gW(kY5?retkLbF=>p=59Nl|=sf zo1Pc|{{N4>5nt#627ylGF`3n>X%`w%bw-Y~zWM_{Si$dc82|=YhISal{N7OY?O`C4 zD|qb}6nLWJ`hUyL+E>-;ricg9J@ZNYP(x(Sct&OI$Y!QWr*=^VN;G3#i>^1n4e#Je zOVhbFbLpXVu*16enDM+ic;97@R~u&kh__kgP#!R`*rQEnA+_dLkNP~L`0alC|J;c; zeiK=s8;BsLE)KbG3BD&Br@(Ha@SBT&$?xX`=$;eeel=|R_dIr6-Ro?=HEjnsJ_b`1 zK6Yg^-6;^2aW!xeTK)A~3Rm|L^FCHB_I>jIju7ZGo&N_1*QHkxH2!!%@o4iZ?vntS;&zJdPe1dH#04YD93A44o-MpfD zP{rn_aq>U%RDvC2+bp;xPlsOzauIi3*Lf42`jVKKZCRuKdYhi>FDuL2l=v{$BCN#Q6796s%r-AG$Q^t(3c@ zD?w0UhYr11@feiyl9kY_@H8~|xlmO<8PfQmj1!$@WieW@VxR@Psxfe-v9WCi1+f>F4VL?0O~K7T?m4-u|pSkBpUJZZe*16_wAp zSYZ@;k`3;W3UHKUWc8QeI}0jH5Ly=cGWQPw(Kr2fm=-5L(d`lcXofy8tJY3@Tuadz zYWXR{mW7XT!RF#RVCe%}=tM*O6!AD3^(!8un~opNI%Uko7$5t@<8+?; zTxDys(MyyGsUjtSu9$+|_-t!U3fVb1dkK?l`17<+jfl=hrBHnDSV>^R1=TnQeyqbW z>ov#l%!1|S!1>8UUxIdhQq`_klcHVx0{?#>K3#$4GlXncwldt!g17TcvKq-jo_996 z>oA=tH9CqRl6Yw?Uc`am!V?lHJbizOJaVaScf1UP5e7Dbgabq=b!B~T&_F6?ooU>w%x0A zH~&MHJ=q`fCH{U<7MDXE4SD32cDZA)WJeWkllJ`UspWaS#eDe^kg^oU_A14UE9zG-a^g{xaXf$})Wik>gT zl#dkzGr(;h0JZDuFn(+k8wNq?PZ5grQ<+sM?wBGt@JnH6v0#or-5wBQWKU~(S_> zkE!tc*ZJ1Y&*p(xX84POb3cClRMd!^qJ#CAZfIepEj-<`VURS_yCz0(?*Ixcj4 z-!zV1_QZhpm=0<;*(nm+F>T=)o?ep@CK5I%g^VAA+RB25ab?7)A~z~egru=I1S|@v zH7tXV!0wmGS^qj#e+MY;C5eUjEAp$Y?LDkS^QPZ}8WN85?r$u<-Epi;yZ1|J2J`se z$D6DpH~2F=eI0B&=UFAUnJvZAmClJlK)sutJ?M>xpZiWV&0=G4MZP+x+p>EX=HbCz zxls%Mw?*u^;LbHWIWCyq+yi)`GmFn9J112CZda_u@YIP%i;srFg_paU02Ifij*7}l z&CF-(3|>*a|+vbNR`^RP=9G?ymEJ0Z~)d&c*UE$UMepZ zcITr{0WqhxkjUnM15js_gW=e3Uh|y6ZReaXHIz-=p`x5VvB&rH9y>Amv@^WmXFEw) zQXYrk3feir=a{jMQ+wDIkkFnZ$k{sJakHn*?u za%4b!00ev8NVLM1TY=cl?KB&55BY_MU-sg?c>=Dbz_W{(Z~c?HJi*XpYL)C6Bd8WH zt+v-#0&o~@t4qESi*)+eW%@VD0|o^yF)n0hME$UtXF$*Lvh}7sso{`|pn*JDIy5^Fm3s$5*zEE=?u5<=l8FJc3r%+H} zdfoNl2J0^~!-*mOL5o-x32|e0Im*E!yY7F7E5N)W3>+v_LBydlEx?4$RL5f2oYRD# zaR0wv(-p~wO0eLDl3K=%`{5+0Gd$ktO=W)gWlGZJ0`K z$_RNA=ckrfa;H0KA~dR^p�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/templates/testappres/res/values-night/themes.xml b/templates/testappres/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/templates/testappres/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/templates/testappres/res/values/colors.xml b/templates/testappres/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/templates/testappres/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/templates/testappres/res/values/stringsclient.xml.tpl b/templates/testappres/res/values/stringsclient.xml.tpl new file mode 100644 index 0000000..7147c01 --- /dev/null +++ b/templates/testappres/res/values/stringsclient.xml.tpl @@ -0,0 +1,3 @@ + + {{Camel .Module.Name}}TestClientApp + \ No newline at end of file diff --git a/templates/testappres/res/values/stringsservice.xml.tpl b/templates/testappres/res/values/stringsservice.xml.tpl new file mode 100644 index 0000000..3ee5096 --- /dev/null +++ b/templates/testappres/res/values/stringsservice.xml.tpl @@ -0,0 +1,3 @@ + + {{Camel .Module.Name}}TestServiceApp + \ No newline at end of file diff --git a/templates/testappres/res/values/themes.xml b/templates/testappres/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/templates/testappres/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/templates/testappres/res/xml/backup_rules.xml b/templates/testappres/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/templates/testappres/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/templates/testappres/res/xml/data_extraction_rules.xml b/templates/testappres/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/templates/testappres/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/templates/testclientapp/application.java.tpl b/templates/testclientapp/application.java.tpl index caaf5c0..bb79760 100644 --- a/templates/testclientapp/application.java.tpl +++ b/templates/testclientapp/application.java.tpl @@ -39,7 +39,7 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ //TODO ALIGN TO YOUR APP private static String mModuleNameUnreal = "com.example.TestAndroid"; - private static String mModuleNameStub = "{{dot .Module.Name}}.{{dot .Module.Name}}_service_example.{{Camel .Module.Name}}TestServiceApp"; + private static String mModuleNameStub = "{{dot .Module.Name}}.{{dot .Module.Name}}serviceexample.{{Camel .Module.Name}}TestServiceApp"; private TextView outputTextViewProp; private TextView outputTextViewSig; private TextView outputTextVieMethodRes; @@ -234,7 +234,7 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ @Override public void on{{Camel .Name}}({{javaParams "" .Params}}) { - String text = "Signal {{.Name}} {{- range .Params -}}" + " " + {{javaVar .}}{{ end}}; + String text = "Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}; outputTextViewSig.setText(text); Log.w(TAG, text); } diff --git a/templates/testserviceapp/AndroidManifest.xml.tpl b/templates/testserviceapp/AndroidManifest.xml.tpl new file mode 100644 index 0000000..4dca8ce --- /dev/null +++ b/templates/testserviceapp/AndroidManifest.xml.tpl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + {{- range .Module.Interfaces -}} + + + {{- end }} + + + diff --git a/templates/testserviceapp/application.java.tpl b/templates/testserviceapp/application.java.tpl new file mode 100644 index 0000000..c748d5b --- /dev/null +++ b/templates/testserviceapp/application.java.tpl @@ -0,0 +1,224 @@ +package {{dot .Module.Name}}.{{dot .Module.Name}}serviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added +{{- $Interface := (index .Module.Interfaces 0) }} + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $Interface.Name }}ServiceAdapter; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $Interface.Name}}ServiceFactory; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $Interface.Name }}ServiceStarter; + +//import message type and parcelabe types +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- end }} + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel $Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_impl.{{Camel $Interface.Name}}Service; +import java.util.concurrent.CompletableFuture; + + + +public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{{Camel $Interface.Name }}EventListener +{ + + private static final String TAG = "{{Camel .Module.Name}}TestServiceApp"; + static Intent stub_service = null; + + + private I{{Camel $Interface.Name}} mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + {{- range $Interface.Properties }} + Button b{{Camel .Name}} = new Button(this); + b{{Camel .Name}}.setText("Set {{.Name}}"); + b{{Camel .Name}}.setBackgroundColor(Color.GREEN); + + b{{Camel .Name}}.setOnClickListener(v -> { + {{javaReturn "" . }} new{{Camel .Name}} = mBackend.get{{Camel .Name}}(); + //TODO increment + Log.i(TAG, "SET {{.Name}}" + new{{Camel .Name}}); + mBackend.set{{Camel .Name}}(new{{Camel .Name}}); + }); + propertyButtonsLine.addView(b{{Camel .Name}}); + {{- end }} + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + {{- range $Interface.Signals }} + Button b{{Camel .Name}} = new Button(this); + b{{Camel .Name}}.setText("{{.Name}}"); + + b{{Camel .Name}}.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal {{.Name}} "); + {{- range .Params}} + {{javaType "" . }} {{javaVar .}} = {{javaTestValue "" .}}; + {{- end}} + mBackend.fire{{Camel .Name}}({{javaVars .Params}}); + }); + b{{Camel .Name}}.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(b{{Camel .Name}}); + {{- end }} + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, {{Camel $Interface.Name }}ServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = {{Camel $Interface.Name }}ServiceAdapter.setService({{Camel $Interface.Name }}ServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + + {{- range $Interface.Properties }} + @Override + public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) + { + outputTextViewProp.setText("Property from service: {{.Name}} " + newValue); + Log.w(TAG, "Property from service: {{.Name}} " + newValue); + } + {{- end }} + {{- range $Interface.Signals }} + @Override + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + String text = "Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + {{- end }} + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/templates/testserviceapp/build.gradle.tpl b/templates/testserviceapp/build.gradle.tpl new file mode 100644 index 0000000..cfbaf0a --- /dev/null +++ b/templates/testserviceapp/build.gradle.tpl @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace '{{dot .Module.Name}}.{{dot .Module.Name}}serviceexample' + compileSdk 35 + + defaultConfig { + applicationId "{{dot .Module.Name}}.{{dot .Module.Name}}serviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':{{camel .Module.Name}}_api') + implementation project(':{{camel .Module.Name}}_android_messenger') + implementation project(':{{camel .Module.Name}}_android_service') + implementation project(':{{camel .Module.Name}}_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/templates/testserviceapp/proguard-rules.pro b/templates/testserviceapp/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/templates/testserviceapp/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file From 9f37128b95e6da61575e9de76acfc0c0afd78e6f Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:55:59 +0200 Subject: [PATCH 25/45] feat(jni): add client jni bridge It is an event listener that has native methods that needs to be implemented with cpp code. It allows adding e.g. unreal client adapter. There are no project files added as unreal provides its own build system. This is part of adding the android-jni feature. --- rules.yaml | 5 + .../jnibridge/client/jnibridgeclient.java.tpl | 140 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 templates/jnibridge/client/jnibridgeclient.java.tpl diff --git a/rules.yaml b/rules.yaml index b808420..3716626 100644 --- a/rules.yaml +++ b/rules.yaml @@ -135,6 +135,11 @@ features: target: "{{Camel .Interface.Name}}JniService.java" - source: "jnibridge/service/jnibridgeservicestarter.java.tpl" target: "{{Camel .Interface.Name }}JniServiceStarter.java" + - match: interface + prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}jniclient/" + documents: + - source: "jnibridge/client/jnibridgeclient.java.tpl" + target: "{{Camel .Interface.Name}}JniClient.java" - name: testclientapp requires: - api diff --git a/templates/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl new file mode 100644 index 0000000..8447c03 --- /dev/null +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -0,0 +1,140 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}jniclient; + +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Interface.Name }}Client; + +{{- range .Module.Structs }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interface.Name }} implements I{{Camel .Interface.Name }}EventListener +{ + + private static final String TAG = "{{Camel .Interface.Name}}JniClient"; + + private {{Camel .Interface.Name }}Client mMessengerClient = null; + + + private static String ModuleName = "{{camel .Module.Name}}.{{camel .Module.Name}}jniservice.{{Camel .Interface.Name}}JniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + + {{- range .Interface.Properties }} + @Override + public void set{{Camel .Name}}({{javaParam "" .}}) + { + Log.i(TAG, "got request from ue, set{{Camel .Name}}" + ({{javaVar .}})); + mMessengerClient.set{{Camel .Name}}({{javaVar .}}); + } + @Override + public {{javaReturn "" . }} get{{Camel .Name}}() + { + Log.i(TAG, "got request from ue, get{{Camel .Name}}"); + return mMessengerClient.get{{Camel .Name}}(); + } + {{ end }} + + {{- range .Interface.Operations }} + public {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}) + { + Log.v(TAG, "Blocking call{{camel .Name}} - should not be used "); + {{if not .Return.IsVoid}}return{{ end }} mMessengerClient.{{camel .Name}}({{javaVars .Params}}); + } + + public void {{camel .Name}}Async(String callId{{if len .Params}}, {{javaParams "" .Params}}{{end}}){ + Log.v(TAG, "non blocking call {{camel .Name}} "); + mMessengerClient.{{camel .Name}}Async({{javaVars .Params }}).thenAccept(i -> { + nativeOn{{Camel .Name}}Result({{if not .Return.IsVoid}}i, {{end}}callId);}); + } + + //Should not be called directly, use {{camel .Name}}Async(String callId, {{javaParams "" .Params}}) + public {{javaAsyncReturn "" .Return}} {{camel .Name}}Async({{javaParams "" .Params}}) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.{{camel .Name}}Async({{javaVars .Params }}); + } + {{- end }} + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new {{Camel .Interface.Name }}Client(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + {{- range .Interface.Properties }} + @Override + public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOn{{Camel .Name}}Changed(newValue); + } + {{- end }} + + {{- range .Interface.Signals }} + @Override + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}); + nativeOn{{Camel .Name}}({{javaVars .Params }}); + } + {{- end }} + + + {{- range .Interface.Properties }} + private native void nativeOn{{Camel .Name}}Changed({{javaParam "" . }}); + {{- end }} + + {{- range .Interface.Signals }} + private native void nativeOn{{Camel .Name}}({{javaParams "" .Params }}); + {{- end }} + {{- range .Interface.Operations }} + private native void nativeOn{{Camel .Name}}Result({{if not .Return.IsVoid}}{{javaReturn "" .Return}} result, {{end}}String callId); + {{- end }} + private native void nativeIsReady(boolean isReady); +} From 7c98cad9e5c27fa30f73b004b4813dc9fe9c252c Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:00:21 +0200 Subject: [PATCH 26/45] docs: update on implemented features --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1da7922..948298e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ApiGear Java Template This is a template for creating a Java project using ApiGear. +For more details on *ApiGear* please visit [apigear.io](https://apigear.io) or the [*ApiGear* documentation](https://docs.apigear/). ## Usage @@ -28,17 +29,19 @@ See the [ApiGear documentation](https://apigear.io/docs/intro) for more informat ## Supported Features -This is a very basic template. It supports only the core features of ApiGear. +This is not fully implemented template. It supports only some of the ApiGear features. -## Features - -- API: - - Simple API using interfaces, enums, POJOs, etc. +The template offers the following feature switches which can be enabled during code generation: +* `api`: create interface files and an abstract implementation +* `stubs`: create a stub implementation, that handles all the properties, but is missing the business logic. +* `android`: create the adaptation layer for android messenger communication. Produces the client and the service sides for the interfaces. +* `jnibridge`: create the java jni implementation, that is missing the c++ side and build files. See example of using it in the jni feature in unreal template. +* `testclientapp`: create example messenger client application. This is not ready to use example - the logic for generated buttons should be filled (which values to send), the rest is already there. The app serves only first defined interface. +* `testserviceapp`: create example messenger service application. This is not ready to use example - the logic for generated buttons should be filled (which values to send), the rest is already there. The app serves only first defined interface. ## Planned Features - github workflow -- scaffold: maven/gradle support - demo app - imports: support for importing other APIs - extends: support for extending other APIs From 43dc33ec43b3a41fbc67f6f8e925e878658b6ebc Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 29 Aug 2025 09:06:23 +0200 Subject: [PATCH 27/45] fix: change module name filter --- rules.yaml | 52 +++++++++---------- templates/android/client/client.java.tpl | 16 +++--- templates/android/client/clienttest.java.tpl | 16 +++--- .../android/messenger/enumparcelable.java.tpl | 4 +- .../messenger/structparcelable.java.tpl | 4 +- .../service/implservicefactory.java.tpl | 4 +- .../service/implservicestarter.java.tpl | 4 +- .../android/service/iservicefactory.java.tpl | 2 +- .../android/service/serviceadapter.java.tpl | 16 +++--- .../service/serviceadaptertest.java.tpl | 16 +++--- templates/api/abstract.java.tpl | 10 ++-- templates/api/additions.gradle.tpl | 2 +- templates/api/enum.java.tpl | 2 +- templates/api/eventlistener.java.tpl | 6 +-- templates/api/interface.java.tpl | 8 +-- templates/api/struct.java.tpl | 2 +- .../jnibridge/client/jnibridgeclient.java.tpl | 10 ++-- .../service/jnibridgeservice.java.tpl | 10 ++-- .../service/jnibridgeservicefactory.java.tpl | 4 +- .../service/jnibridgeservicestarter.java.tpl | 4 +- templates/settings.gradle.tpl | 4 +- templates/stub/implservice.java.tpl | 10 ++-- templates/testclientapp/application.java.tpl | 14 ++--- templates/testclientapp/build.gradle.tpl | 4 +- templates/testserviceapp/application.java.tpl | 16 +++--- templates/testserviceapp/build.gradle.tpl | 4 +- 26 files changed, 122 insertions(+), 122 deletions(-) diff --git a/rules.yaml b/rules.yaml index 3716626..4c59cae 100644 --- a/rules.yaml +++ b/rules.yaml @@ -3,7 +3,7 @@ features: - name: api scopes: - match: module - prefix: "{{dot .Module.Name}}/" + prefix: "{{camel .Module.Name}}/" documents: - source: "libs.versions.toml" target: "gradle/libs.versions.toml" @@ -14,11 +14,11 @@ features: target: "gradle.properties" raw: true - source: "api/build.gradle.tpl" - target: "{{dot .Module.Name}}_api/build.gradle" + target: "{{camel .Module.Name}}_api/build.gradle" - source: "api/additions.gradle.tpl" - target: "{{dot .Module.Name}}_api/additions.gradle" + target: "{{camel .Module.Name}}_api/additions.gradle" - match: interface - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_api/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_api/" documents: - source: "api/eventlistener.java.tpl" target: "I{{Camel .Interface.Name }}EventListener.java" @@ -27,12 +27,12 @@ features: - source: "api/interface.java.tpl" target: "I{{Camel .Interface.Name}}.java" - match: struct - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_api/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_api/" documents: - source: "api/struct.java.tpl" target: "{{Camel .Struct.Name }}.java" - match: enum - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_api/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_api/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_api/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_api/" documents: - source: "api/enum.java.tpl" target: "{{Camel .Enum.Name }}.java" @@ -41,14 +41,14 @@ features: - api scopes: - match: module - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_impl/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_impl/" documents: - source: "stub/build.gradle.tpl" target: "build.gradle" - source: "stub/additions.gradle.tpl" target: "additions.gradle" - match: interface - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_impl/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_impl/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_impl/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_impl/" documents: - source: "stub/implservice.java.tpl" target: "{{Camel .Interface.Name}}Service.java" @@ -58,7 +58,7 @@ features: - stubs scopes: - match: module - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_service/" documents: - source: "android/service/build.gradle.tpl" target: "build.gradle" @@ -67,7 +67,7 @@ features: - source: "android/service/AndroidManifest.xml.tpl" target: "src/main/AndroidManifest.xml" - match: interface - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_service/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_service/" documents: - source: "android/service/iservicefactory.java.tpl" target: "I{{Camel .Interface.Name}}ServiceFactory.java" @@ -78,46 +78,46 @@ features: - source: "android/service/implservicestarter.java.tpl" target: "{{Camel .Interface.Name }}ServiceStarter.java" - match: interface - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/src/test/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_service/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_service/src/test/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_service/" documents: - source: "android/service/serviceadaptertest.java.tpl" target: "{{Camel .Interface.Name}}ServiceAdapterTest.java" - match: module - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/" documents: - source: "android/messenger/build.gradle.tpl" target: "build.gradle" - source: "android/messenger/additions.gradle.tpl" target: "additions.gradle" - match: interface - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_messenger/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/" documents: - source: "android/messenger/messages.java.tpl" target: "{{Camel .Interface.Name}}MessageType.java" - match: struct - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/" documents: - source: "android/messenger/structparcelable.java.tpl" target: "{{Camel .Struct.Name }}Parcelable.java" - match: enum - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/src/main/java/{{dot .Module.Name}}/{{dot .Module.Name}}_android_messenger/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/" documents: - source: "android/messenger/enumparcelable.java.tpl" target: "{{Camel .Enum.Name }}Parcelable.java" - match: module - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_client/" documents: - source: "android/client/build.gradle.tpl" target: "build.gradle" - source: "android/client/additions.gradle.tpl" target: "additions.gradle" - match: interface - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/src/main/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_client/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_client/" documents: - source: "android/client/client.java.tpl" target: "{{Camel .Interface.Name}}Client.java" - match: interface - prefix: "{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/src/test/java/{{dot .Module.Name}}/{{camel .Module.Name}}_android_client/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_client/src/test/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_client/" documents: - source: "android/client/clienttest.java.tpl" target: "{{Camel .Interface.Name}}ClientTest.java" @@ -127,7 +127,7 @@ features: - android scopes: - match: interface - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}jniservice/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}jniservice/" documents: - source: "jnibridge/service/jnibridgeservicefactory.java.tpl" target: "{{Camel .Interface.Name}}JniServiceFactory.java" @@ -136,7 +136,7 @@ features: - source: "jnibridge/service/jnibridgeservicestarter.java.tpl" target: "{{Camel .Interface.Name }}JniServiceStarter.java" - match: interface - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}jniclient/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}jniclient/" documents: - source: "jnibridge/client/jnibridgeclient.java.tpl" target: "{{Camel .Interface.Name}}JniClient.java" @@ -146,17 +146,17 @@ features: - android scopes: - match: module - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_client_example/" documents: - source: "testclientapp/build.gradle.tpl" target: "build.gradle" - match: module - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/src/main/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_client_example/src/main/" documents: - source: "testclientapp/AndroidManifest.xml.tpl" target: "AndroidManifest.xml" - source: "testclientapp/application.java.tpl" - target: "java/{{dot .Module.Name}}/{{dot .Module.Name}}_client_example/{{Camel .Module.Name}}TestClientApp.java" + target: "java/{{camel .Module.Name}}/{{camel .Module.Name}}_client_example/{{Camel .Module.Name}}TestClientApp.java" - source: "testappres/res/drawable/ic_launcher_background.xml" target: "res/drawable/ic_launcher_background.xml" raw: true @@ -223,17 +223,17 @@ features: - android scopes: - match: module - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}serviceexample/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}serviceexample/" documents: - source: "testserviceapp/build.gradle.tpl" target: "build.gradle" - match: module - prefix: "{{dot .Module.Name}}/{{dot .Module.Name}}serviceexample/src/main/" + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}serviceexample/src/main/" documents: - source: "testserviceapp/AndroidManifest.xml.tpl" target: "AndroidManifest.xml" - source: "testserviceapp/application.java.tpl" - target: "java/{{dot .Module.Name}}/{{dot .Module.Name}}serviceexample/{{Camel .Module.Name}}TestServiceApp.java" + target: "java/{{camel .Module.Name}}/{{camel .Module.Name}}serviceexample/{{Camel .Module.Name}}TestServiceApp.java" - source: "testappres/res/drawable/ic_launcher_background.xml" target: "res/drawable/ic_launcher_background.xml" raw: true diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 832e0b9..272edb4 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -19,18 +19,18 @@ import android.util.Log; //import message type and parcelabe types {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index d680f92..00bdd68 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -6,18 +6,18 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Int //import message type and parcelabe types {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; import android.content.Context; import android.os.Bundle; diff --git a/templates/android/messenger/enumparcelable.java.tpl b/templates/android/messenger/enumparcelable.java.tpl index 900ac6a..72bdc06 100644 --- a/templates/android/messenger/enumparcelable.java.tpl +++ b/templates/android/messenger/enumparcelable.java.tpl @@ -1,6 +1,6 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger; +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Enum.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Enum.Name}}; import android.os.Parcel; import android.os.Parcelable; diff --git a/templates/android/messenger/structparcelable.java.tpl b/templates/android/messenger/structparcelable.java.tpl index c455f3a..2bdeea7 100644 --- a/templates/android/messenger/structparcelable.java.tpl +++ b/templates/android/messenger/structparcelable.java.tpl @@ -1,6 +1,6 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger; +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Struct.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Struct.Name}}; import android.os.Parcel; import android.os.Parcelable; diff --git a/templates/android/service/implservicefactory.java.tpl b/templates/android/service/implservicefactory.java.tpl index 27c9a49..7b5fe05 100644 --- a/templates/android/service/implservicefactory.java.tpl +++ b/templates/android/service/implservicefactory.java.tpl @@ -3,8 +3,8 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name }}ServiceFactory; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name }}; import {{camel .Module.Name}}.{{camel .Module.Name}}_impl.{{Camel .Interface.Name}}Service; import android.util.Log; import android.os.HandlerThread; diff --git a/templates/android/service/implservicestarter.java.tpl b/templates/android/service/implservicestarter.java.tpl index 54106b3..e6df578 100644 --- a/templates/android/service/implservicestarter.java.tpl +++ b/templates/android/service/implservicestarter.java.tpl @@ -4,8 +4,8 @@ import android.util.Log; import android.content.Context; import android.content.Intent; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name}}ServiceFactory; diff --git a/templates/android/service/iservicefactory.java.tpl b/templates/android/service/iservicefactory.java.tpl index 7b5c5fc..12561ca 100644 --- a/templates/android/service/iservicefactory.java.tpl +++ b/templates/android/service/iservicefactory.java.tpl @@ -1,5 +1,5 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; public interface I{{Camel .Interface.Name}}ServiceFactory { diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index fd0b43a..4a79d57 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -16,19 +16,19 @@ import android.os.RemoteException; import android.util.Log; {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 9c52393..e8079c2 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -19,20 +19,20 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .In //import message type and parcelabe types {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Interface.Name}}MessageType; import static org.junit.Assert.assertEquals; diff --git a/templates/api/abstract.java.tpl b/templates/api/abstract.java.tpl index adf1c7e..61a9469 100644 --- a/templates/api/abstract.java.tpl +++ b/templates/api/abstract.java.tpl @@ -1,13 +1,13 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_api; +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; //TODO imported/extern modules {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} import java.util.Collection; diff --git a/templates/api/additions.gradle.tpl b/templates/api/additions.gradle.tpl index c36c0da..e3f0f90 100644 --- a/templates/api/additions.gradle.tpl +++ b/templates/api/additions.gradle.tpl @@ -1,5 +1,5 @@ android { - namespace '{{camel .Module.Name}}.{{dot .Module.Name}}_api' + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_api' } dependencies { diff --git a/templates/api/enum.java.tpl b/templates/api/enum.java.tpl index 759cabd..a16a3e8 100644 --- a/templates/api/enum.java.tpl +++ b/templates/api/enum.java.tpl @@ -1,4 +1,4 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_api; +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/templates/api/eventlistener.java.tpl b/templates/api/eventlistener.java.tpl index e149fe4..d79df68 100644 --- a/templates/api/eventlistener.java.tpl +++ b/templates/api/eventlistener.java.tpl @@ -1,10 +1,10 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_api; +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} public interface I{{Camel .Interface.Name }}EventListener { diff --git a/templates/api/interface.java.tpl b/templates/api/interface.java.tpl index 2134b66..d43605d 100644 --- a/templates/api/interface.java.tpl +++ b/templates/api/interface.java.tpl @@ -1,11 +1,11 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_api; +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} import java.util.concurrent.CompletableFuture; diff --git a/templates/api/struct.java.tpl b/templates/api/struct.java.tpl index 1448a7f..03a64c2 100644 --- a/templates/api/struct.java.tpl +++ b/templates/api/struct.java.tpl @@ -1,4 +1,4 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_api; +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/templates/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl index 8447c03..9a004ef 100644 --- a/templates/jnibridge/client/jnibridgeclient.java.tpl +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -1,16 +1,16 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}jniclient; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Interface.Name }}Client; {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} import android.content.Context; diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl index 6a68298..44ca6ef 100644 --- a/templates/jnibridge/service/jnibridgeservice.java.tpl +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -3,14 +3,14 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}jniservice; import android.os.Messenger; import android.util.Log; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} diff --git a/templates/jnibridge/service/jnibridgeservicefactory.java.tpl b/templates/jnibridge/service/jnibridgeservicefactory.java.tpl index 1746e4e..87e2631 100644 --- a/templates/jnibridge/service/jnibridgeservicefactory.java.tpl +++ b/templates/jnibridge/service/jnibridgeservicefactory.java.tpl @@ -3,8 +3,8 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}jniservice; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name }}ServiceFactory; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name }}; import {{camel .Module.Name}}.{{camel .Module.Name}}jniservice.{{Camel .Interface.Name}}JniService; import android.util.Log; import android.os.HandlerThread; diff --git a/templates/jnibridge/service/jnibridgeservicestarter.java.tpl b/templates/jnibridge/service/jnibridgeservicestarter.java.tpl index 0b335ac..093d4ab 100644 --- a/templates/jnibridge/service/jnibridgeservicestarter.java.tpl +++ b/templates/jnibridge/service/jnibridgeservicestarter.java.tpl @@ -4,8 +4,8 @@ import android.util.Log; import android.content.Context; import android.content.Intent; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .Interface.Name }}ServiceAdapter; import {{camel .Module.Name}}.{{camel .Module.Name}}jniservice.{{Camel .Interface.Name}}JniServiceFactory; diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index b9e32e5..e8c38aa 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -32,8 +32,8 @@ include ':{{camel .Module.Name}}_impl' include ':{{camel .Module.Name}}_api' {{- end -}} {{- if .Features.testclientapp }} -include ':{{dot .Module.Name}}_client_example' +include ':{{camel .Module.Name}}_client_example' {{- end -}} {{- if .Features.testserviceapp }} -include ':{{dot .Module.Name}}serviceexample' +include ':{{camel .Module.Name}}serviceexample' {{- end -}} diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index cc8779d..d61d197 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -3,14 +3,14 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_impl; import android.os.Messenger; import android.util.Log; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} diff --git a/templates/testclientapp/application.java.tpl b/templates/testclientapp/application.java.tpl index bb79760..8efdbb5 100644 --- a/templates/testclientapp/application.java.tpl +++ b/templates/testclientapp/application.java.tpl @@ -1,4 +1,4 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}_client_example; +package {{camel .Module.Name}}.{{camel .Module.Name}}_client_example; import android.app.Activity; import android.graphics.Color; @@ -17,15 +17,15 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel $Int //import message type and parcelabe types {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; import java.util.concurrent.CompletableFuture; @@ -39,7 +39,7 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ //TODO ALIGN TO YOUR APP private static String mModuleNameUnreal = "com.example.TestAndroid"; - private static String mModuleNameStub = "{{dot .Module.Name}}.{{dot .Module.Name}}serviceexample.{{Camel .Module.Name}}TestServiceApp"; + private static String mModuleNameStub = "{{camel .Module.Name}}.{{camel .Module.Name}}serviceexample.{{Camel .Module.Name}}TestServiceApp"; private TextView outputTextViewProp; private TextView outputTextViewSig; private TextView outputTextVieMethodRes; diff --git a/templates/testclientapp/build.gradle.tpl b/templates/testclientapp/build.gradle.tpl index 397e103..873a2d5 100644 --- a/templates/testclientapp/build.gradle.tpl +++ b/templates/testclientapp/build.gradle.tpl @@ -3,11 +3,11 @@ plugins { } android { - namespace '{{dot .Module.Name}}.{{dot .Module.Name}}_client_example' + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_client_example' compileSdk 35 defaultConfig { - applicationId "{{dot .Module.Name}}.{{dot .Module.Name}}_client_example" + applicationId "{{camel .Module.Name}}.{{camel .Module.Name}}_client_example" minSdk 33 targetSdk 35 versionCode 1 diff --git a/templates/testserviceapp/application.java.tpl b/templates/testserviceapp/application.java.tpl index c748d5b..a56be85 100644 --- a/templates/testserviceapp/application.java.tpl +++ b/templates/testserviceapp/application.java.tpl @@ -1,4 +1,4 @@ -package {{dot .Module.Name}}.{{dot .Module.Name}}serviceexample; +package {{camel .Module.Name}}.{{camel .Module.Name}}serviceexample; import android.app.Activity; import android.graphics.Color; @@ -20,17 +20,17 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $In //import message type and parcelabe types {{- range .Module.Structs }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} {{- range .Module.Enums }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.{{Camel .Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; -import {{dot .Module.Name}}.{{dot .Module.Name}}_api.I{{Camel $Interface.Name}}; -import {{dot .Module.Name}}.{{dot .Module.Name}}_impl.{{Camel $Interface.Name}}Service; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel $Interface.Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_impl.{{Camel $Interface.Name}}Service; import java.util.concurrent.CompletableFuture; diff --git a/templates/testserviceapp/build.gradle.tpl b/templates/testserviceapp/build.gradle.tpl index cfbaf0a..d93347e 100644 --- a/templates/testserviceapp/build.gradle.tpl +++ b/templates/testserviceapp/build.gradle.tpl @@ -3,11 +3,11 @@ plugins { } android { - namespace '{{dot .Module.Name}}.{{dot .Module.Name}}serviceexample' + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}serviceexample' compileSdk 35 defaultConfig { - applicationId "{{dot .Module.Name}}.{{dot .Module.Name}}serviceexample" + applicationId "{{camel .Module.Name}}.{{camel .Module.Name}}serviceexample" minSdk 33 targetSdk 35 versionCode 1 From 80170179e003f2f4416251787580d7e1ab4af69a Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:31:39 +0200 Subject: [PATCH 28/45] feat: structs support complex and nested struct as data fields --- .../messenger/structparcelable.java.tpl | 71 ++++++++++++++----- .../service/serviceadaptertest.java.tpl | 3 +- templates/api/struct.java.tpl | 49 ++++++++++--- 3 files changed, 93 insertions(+), 30 deletions(-) diff --git a/templates/android/messenger/structparcelable.java.tpl b/templates/android/messenger/structparcelable.java.tpl index 2bdeea7..7a868dc 100644 --- a/templates/android/messenger/structparcelable.java.tpl +++ b/templates/android/messenger/structparcelable.java.tpl @@ -4,16 +4,25 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Struct.Name}}; import android.os.Parcel; import android.os.Parcelable; - - -//TODO imports - may need some struct from this or imported module +{{- $typesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Struct.Fields }} +{{- if and (and (not .IsArray) (not .Schema.Import)) (not (or (.IsPrimitive) (eq .KindType "enum")) ) }} +{{- $type := Camel (javaType "" .) }} +{{- $typesToImport = (appendList $typesToImport $type) }} +{{- end }} +{{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +{{- end}} public class {{Camel .Struct.Name}}Parcelable implements Parcelable { public {{Camel .Struct.Name}} data; public {{Camel .Struct.Name}}Parcelable({{Camel .Struct.Name}} data) { - this.data = data; + this.data = new {{Camel .Struct.Name}}(data); } public {{Camel .Struct.Name}} get{{Camel .Struct.Name}}() @@ -22,18 +31,31 @@ import android.os.Parcelable; } protected {{Camel .Struct.Name}}Parcelable(Parcel in) { - + this.data = new {{Camel .Struct.Name}}(); {{- range .Struct.Fields }} -{{- if .IsPrimitive }} - {{javaType "" .}} l_{{camel .Name}} = in.read{{ ( Camel (javaType "" .) ) }}(); +{{- if .IsArray}} +{{- if (eq .KindType "enum") }} + {{javaElementType "" . }}Parcelable[] l_parcelable{{camel .Name}} = in.createTypedArray({{javaElementType "" . }}Parcelable.CREATOR); + data.{{camel .Name}} = {{javaElementType "" . }}Parcelable.unwrapArray(l_parcelable{{camel .Name}}); +{{- else if .IsPrimitive }} + data.{{camel .Name}} = in.create{{ ( Camel (javaElementType "" .) ) }}Array(); +{{- else }} + {{javaElementType "" . }}Parcelable[] l_parcelable{{camel .Name}} = in.createTypedArray({{javaElementType "" . }}Parcelable.CREATOR); + data.{{camel .Name}} = {{javaElementType "" . }}Parcelable.unwrapArray(l_parcelable{{camel .Name}}); +{{- end }} +{{- else }} +{{- if (eq .KindType "enum") }} + {{javaType "" .}}Parcelable l_parcelable{{camel .Name}} = in.readParcelable({{javaType "" .}}Parcelable.class.getClassLoader(), {{javaType "" .}}Parcelable.class); + data.{{camel .Name}} = l_parcelable{{camel .Name}} != null ? l_parcelable{{camel .Name}}.data : null; +{{- else if .IsPrimitive }} + data.{{camel .Name}} = in.read{{ ( Camel (javaType "" .) ) }}(); +{{- else }} + {{javaType "" .}}Parcelable l_parcelable{{camel .Name}} = in.readParcelable({{javaType "" .}}Parcelable.class.getClassLoader(), {{javaType "" .}}Parcelable.class); + data.{{camel .Name}} = l_parcelable{{camel .Name}} != null ? l_parcelable{{camel .Name}}.data : null; {{- end }} {{- end }} - // TODO arrays in general - // TODO add enums(Arrays) = MyEnumWrapper singleEnumWrapper = in.readParcelable(MyEnumWrapper.class.getClassLoader()); MyEnum singleEnum = singleEnumWrapper != null ? singleEnumWrapper.value : null; - // TODO read other structs same as enums - they all should be parcelable - this.data = new {{Camel .Struct.Name}}( - {{- range $idx, $m :=.Struct.Fields }}{{- if $idx}}, {{ end -}}l_{{camel .Name}}{{- end }}); +{{- end }} } public static final Creator<{{Camel .Struct.Name}}Parcelable> CREATOR = new Creator<{{Camel .Struct.Name}}Parcelable>() { @@ -50,14 +72,27 @@ import android.os.Parcelable; @Override public void writeToParcel(Parcel dest, int flags) { - {{- range .Struct.Fields }} - {{- if .IsPrimitive }} +{{- if .IsArray}} +{{- if (eq .KindType "enum") }} + dest.writeTypedArray({{javaElementType "" . }}Parcelable.wrapArray(data.{{camel .Name}}), flags); +{{- else if .IsPrimitive }} + dest.write{{ ( Camel (javaElementType "" .) ) }}Array(data.{{camel .Name}}); +{{- else }} + dest.writeTypedArray({{javaElementType "" . }}Parcelable.wrapArray(data.{{camel .Name}}), flags); +{{- end }} +{{- else }} +{{- if (eq .KindType "enum") }} + dest.writeParcelable(new {{javaType "" .}}Parcelable(data.{{camel .Name}}), flags); +{{- else if .IsPrimitive }} dest.write{{ ( Camel (javaType "" .) ) }}(data.{{camel .Name}}); - {{- end }} - {{- end }} - // TODO arrays in general - // TODO add enums +{{- else }} + dest.writeParcelable(new {{javaType "" .}}Parcelable(data.{{camel .Name}}), flags); +{{- end }} +{{- end }} + +{{- end}} + } public static {{Camel .Struct.Name}}Parcelable[] wrapArray({{Camel .Struct.Name}}[] structs) { diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index e8079c2..03f71d9 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -364,8 +364,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- end }} assertEquals(receivedByClient, returnedValue); {{- end}} - resp_data.getInt("callId", 0); - assertEquals(receivedByClientId, callId); + assertEquals(callId, resp_data.getInt("callId", 0)); } {{- end}} diff --git a/templates/api/struct.java.tpl b/templates/api/struct.java.tpl index 03a64c2..c4cb7f2 100644 --- a/templates/api/struct.java.tpl +++ b/templates/api/struct.java.tpl @@ -2,27 +2,56 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_api; import com.fasterxml.jackson.annotation.JsonProperty; -//TODO imports - may need some struct from this or imported module +public class {{Camel .Struct.Name}} { - public class {{Camel .Struct.Name}} { - - public {{Camel .Struct.Name}}({{javaParams "" .Struct.Fields}}) { + public {{Camel .Struct.Name}}({{javaParams "" .Struct.Fields}}) + { {{- range .Struct.Fields }} this.{{camel .Name}} = {{camel .Name}}; {{- end }} } - public {{Camel .Struct.Name}}() {} + public {{Camel .Struct.Name}}() + { +{{- range .Struct.Fields }} +{{- if .IsArray}} + this.{{camel .Name}} = new {{javaElementType "" . }}[0]; +{{- else if not .IsPrimitive }} +{{- if (eq .KindType "enum") }} + this.{{camel .Name}} = {{javaType "" . }}.values()[0]; +{{- else }} + this.{{camel .Name}} = new {{javaType "" . }}(); +{{- end}} +{{- end }} +{{- end }} + } + {{- range .Struct.Fields }} @JsonProperty("{{snake .Name}}") public {{javaType "" .}} {{camel .Name}}; {{- end }} - public {{Camel .Struct.Name}}({{Camel .Struct.Name}} other) { + public {{Camel .Struct.Name}}({{Camel .Struct.Name}} other) + { {{- range .Struct.Fields }} - this.{{camel .Name}} = other.{{camel .Name}}; - {{- end }} - //TODO deepcopy of structs and arrays this.x = new sth(other.x); -} +{{- if .IsArray}} +{{- if or .IsPrimitive ((eq .KindType "enum"))}} + this.{{camel .Name}} = java.util.Arrays.copyOf(other.{{camel .Name}}, other.{{camel .Name}}.length); +{{- else }} + this.{{camel .Name}} = new {{javaElementType "" . }}[other.{{camel .Name}}.length]; + for (int i = 0; i < other.{{camel .Name}}.length; i++) + { + this.{{camel .Name}}[i] = new {{javaElementType "" . }}(other.{{camel .Name}}[i]); + } +{{- end }} +{{- else }} +{{- if or .IsPrimitive ((eq .KindType "enum"))}} + this.{{camel .Name}} = other.{{camel .Name}}; +{{- else }} + this.{{camel .Name}} = new {{javaType "" . }}(other.{{camel .Name}}); +{{- end }} +{{- end }} +{{- end }} + } } From 7ae071a64c20f612f76142e4fd1e723dbe957abc Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:32:55 +0200 Subject: [PATCH 29/45] feat: comparison operators for struct Structs have now implemented equals function and hashCode. --- templates/android/client/clienttest.java.tpl | 19 +++++--- .../service/serviceadaptertest.java.tpl | 36 +++++++++++---- templates/api/struct.java.tpl | 44 ++++++++++++++++++- 3 files changed, 84 insertions(+), 15 deletions(-) diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 00bdd68..43f41bb 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -236,8 +236,10 @@ public class {{Camel .Interface.Name }}ClientTest data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); {{- end }} {{template "getReceivedFromBundle" . }} - {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} - assertEquals(received{{javaVar .}}, test{{ javaVar .}}); + assertEquals(received{{javaVar .}}, test{{ javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}} + , 1e-6f{{end -}} + ); } {{- end}} @@ -276,7 +278,6 @@ public class {{Camel .Interface.Name }}ClientTest {{- template "prepareTestValue" .}} {{- end }} {{- if not .Return.IsVoid }} - {{- if .Return.IsArray }} {{javaElementType "" .Return}} elementForResult = {{javaTestValue "" .Return }}; // todo fill if is struct @@ -290,7 +291,6 @@ public class {{Camel .Interface.Name }}ClientTest //TODO fill fields {{- end }} {{- end }} - AtomicBoolean receivedResp = new AtomicBoolean(false); {{javaAsyncReturn "" .Return}} resFuture = testedClient.{{camel .Name}}Async({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}}test{{javaVar $p}}{{- end }}); @@ -299,7 +299,10 @@ public class {{Camel .Interface.Name }}ClientTest {{- if .Return.IsArray }} assertEquals(expectedResult, result); {{- else if and (.Return.IsPrimitive) (not (eq .Return.KindType "string")) }} - assertEquals(expectedResult, result.{{camel (javaType "" .Return)}}Value()); + assertEquals(expectedResult, result.{{camel (javaType "" .Return)}}Value() + {{- if or (or (eq .Return.KindType "float") (eq .Return.KindType "float32") ) (eq .Return.KindType "float64") -}} + , 1e-6f{{end -}} + ); {{- else }} assertEquals(expectedResult, result); {{- end }} @@ -322,7 +325,10 @@ public class {{Camel .Interface.Name }}ClientTest {{- end }} {{- range .Params }} {{template "getReceivedFromBundle" . }} - assertEquals(received{{javaVar .}}, test{{javaVar .}}); + assertEquals(received{{javaVar .}}, test{{javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}} + , 1e-6f{{end -}} + ); {{- end }} int returnedCallId = data.getInt("callId", -1); @@ -332,6 +338,7 @@ public class {{Camel .Interface.Name }}ClientTest Bundle result_data = new Bundle(); result_data.putInt("callId", returnedCallId); + {{- if not .Return.IsVoid }} {{- if .Return.IsPrimitive }} result_data.put{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result", expectedResult); diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 03f71d9..92bd9d8 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -194,7 +194,10 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- end }} {{- range .Interface.Properties}} {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} - assertEquals(received{{javaVar .}}, init{{ javaVar .}}); + assertEquals(received{{javaVar .}}, init{{ javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}}, + 1e-6f{{end -}} + ); {{- end }} } @@ -234,6 +237,15 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } + + {{- if not ( or (len (.Interface.Signals)) ( or ( len (.Interface.Operations)) ( len (.Interface.Properties)))) }} + @Test + public void onlySetupAndTeardown() + { + + } + {{- end }} + {{- range .Interface.Properties }} //TODO do not add when a property is readonly @Test @@ -267,9 +279,11 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest Bundle data = response.getData(); {{template "getReceivedFromBundle" . }} - {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} - assertEquals(received{{javaVar .}}, test{{javaVar .}}); + assertEquals(received{{javaVar .}}, test{{javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}}, + 1e-6f{{end -}} + ); } {{- end}} @@ -296,8 +310,10 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- end }} {{- range .Params }} {{template "getReceivedFromBundle" . }} - {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} - assertEquals(received{{javaVar .}}, test{{ javaVar .}}); + assertEquals(received{{javaVar .}}, test{{ javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}}, + 1e-6f{{end -}} + ); {{- end}} } {{- end}} @@ -362,9 +378,13 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest resp_data.setClassLoader({{Camel .Return.Type}}Parcelable.class.getClassLoader()); {{javaReturn "" .Return }} receivedByClient = resp_data.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); {{- end }} - assertEquals(receivedByClient, returnedValue); - {{- end}} - assertEquals(callId, resp_data.getInt("callId", 0)); + + assertEquals(receivedByClient, returnedValue + {{- if and (not .Return.IsArray) (or (or (eq .Return.KindType "float") (eq .Return.KindType "float32") ) (eq .Return.KindType "float64")) -}}, + 1e-6f{{end -}} + ); + {{- end }} + assertEquals(callId, resp_data.getInt("callId", -1)); } {{- end}} diff --git a/templates/api/struct.java.tpl b/templates/api/struct.java.tpl index c4cb7f2..f3bacda 100644 --- a/templates/api/struct.java.tpl +++ b/templates/api/struct.java.tpl @@ -1,6 +1,8 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_api; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; public class {{Camel .Struct.Name}} { @@ -54,4 +56,44 @@ public class {{Camel .Struct.Name}} { {{- end }} } - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof {{Camel .Struct.Name}})) return false; + {{Camel .Struct.Name}} other = ({{Camel .Struct.Name}}) o; + + return {{- if not (len (.Struct.Fields)) }} true{{else -}} +{{- range $idx, $s :=.Struct.Fields }} +{{- if .IsArray}} + {{ if $idx}}&&{{ end }} Arrays.equals(this.{{camel .Name}}, other.{{camel .Name}}) +{{- else if or .IsPrimitive ((eq .KindType "enum"))}} + {{ if $idx}}&&{{ end }} this.{{camel .Name}} == other.{{camel .Name}} +{{- else }} + {{ if $idx}}&&{{ end }} Objects.equals(this.{{camel .Name}}, other.{{camel .Name}}) +{{- end }} +{{- end }} +{{- end }}; + } + + @Override + public int hashCode() { + int result = 7; +{{- range .Struct.Fields }} +{{- if .IsArray}} + result = 31 * result + Arrays.hashCode({{camel .Name}}); +{{- else if or (eq .KindType "int") (eq .KindType "int32") }} + result = 31 * result + Integer.hashCode({{camel .Name}}); +{{- else if (eq .KindType "string")}} + result = 31 * result + ({{camel .Name}} != null ? {{camel .Name}}.hashCode() : 0); + +{{- else if .IsPrimitive}} + result = 31 * result + {{Camel (javaType "" .)}}.hashCode({{camel .Name}}); +{{- else }} + result = 31 * result + Objects.hashCode({{camel .Name}}); +{{- end }} +{{- end }} + return result; + } + + +} From 7e81cd83f28ca6751565a43667f5e6d9191429c9 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Sat, 30 Aug 2025 15:46:34 +0200 Subject: [PATCH 30/45] feat: improve comparing complex types use better comparing functions for structures and arrays. Use implemented equals() for the strucutres. Tests requires now filling the structures with some data other than default, cause before setting new value and expected notification it is checked for equality (not set in that case). With new compaision that checks the content not addres of object it needs to have some values. --- rules.yaml | 2 + templates/android/client/client.java.tpl | 13 ++++++ templates/android/client/clienttest.java.tpl | 40 ++++++++++--------- .../service/serviceadaptertest.java.tpl | 19 +++++---- templates/api/testhelper.java.tpl | 34 ++++++++++++++++ templates/stub/implservice.java.tpl | 9 ++++- 6 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 templates/api/testhelper.java.tpl diff --git a/rules.yaml b/rules.yaml index 4c59cae..5a1296d 100644 --- a/rules.yaml +++ b/rules.yaml @@ -17,6 +17,8 @@ features: target: "{{camel .Module.Name}}_api/build.gradle" - source: "api/additions.gradle.tpl" target: "{{camel .Module.Name}}_api/additions.gradle" + - source: "api/testhelper.java.tpl" + target: "{{camel .Module.Name}}_api/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_api/{{Camel .Module.Name}}TestHelper.java" - match: interface prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_api/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_api/" documents: diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 272edb4..86bb956 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -39,6 +39,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; {{- define "getDataFromBundle"}} @@ -317,7 +318,13 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface public void set{{Camel .Name}}({{javaParam "" .}}) { Log.i(TAG, "request set{{Camel .Name}} called "+ {{javaVar . }}); + {{- if .IsArray }} + if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) + {{- else if or .IsPrimitive (eq .KindType "enum") }} if (m_{{javaVar .}} != {{javaVar .}}) + {{- else }} + if (! m_{{javaVar .}}.equals({{javaVar .}})) + {{- end }} { Message msg = new Message(); msg.what = {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(); @@ -332,7 +339,13 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface public void on{{Camel .Name}}({{javaParam "" .}}) { Log.i(TAG, "value received from service for {{Camel .Name}} "); + {{- if .IsArray }} + if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) + {{- else if or .IsPrimitive (eq .KindType "enum") }} if (m_{{javaVar .}} != {{javaVar .}}) + {{- else }} + if (! m_{{javaVar .}}.equals({{javaVar .}})) + {{- end }} { m_{{javaVar .}} = {{javaVar .}}; fire{{Camel .Name}}Changed({{javaVar .}}); diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 43f41bb..ed2de85 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -1,5 +1,4 @@ //TODO later// Copyright Epic Games, Inc. All Rights Reserved. - package {{camel .Module.Name}}.{{camel .Module.Name}}_android_client; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Interface.Name }}Client; @@ -9,6 +8,7 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Int import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Module.Name}}TestHelper; {{- range .Module.Enums }} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; @@ -81,16 +81,17 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter {{- define "prepareTestValue"}} {{- if .IsArray }} - {{javaElementType "" .}} element{{ javaVar .}} = {{javaTestValue "" . }}; - // todo fill if is struct - {{javaReturn "" . }} test{{ javaVar .}} = new {{javaReturn "" . }}{element{{ javaVar .}}} ; - {{- else if (.IsPrimitive) }} + {{- if or (.IsPrimitive) (eq .KindType "enum")}} + {{javaType "" .}} test{{ javaVar .}} = new {{javaElementType "" .}}[1]; + test{{ javaVar .}}[0] = {{javaTestValue "" . }}; + {{- else }} + {{javaElementType "" .}}[] test{{ javaVar .}} = new {{javaElementType "" .}}[1]; + test{{ javaVar .}}[0] = {{Camel .Schema.Module.Name}}TestHelper.makeTest{{Camel (javaElementType "" . )}}(); + {{- end}} + {{- else if or (.IsPrimitive) (eq .KindType "enum") }} {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; - {{- else if eq .KindType "enum"}} - {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; {{- else }} - {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; - //TODO fill fields + {{javaReturn "" . }} test{{ javaVar .}} = {{Camel .Schema.Module.Name}}TestHelper.makeTest{{Camel (javaType "" . )}}(); {{- end }} {{- end }} @@ -277,20 +278,23 @@ public class {{Camel .Interface.Name }}ClientTest {{- range .Params }} {{- template "prepareTestValue" .}} {{- end }} + {{- if not .Return.IsVoid }} {{- if .Return.IsArray }} - {{javaElementType "" .Return}} elementForResult = {{javaTestValue "" .Return }}; - // todo fill if is struct - {{javaReturn "" .Return }} expectedResult = new {{javaReturn "" .Return }}{elementForResult} ; - {{- else if (.Return.IsPrimitive) }} - {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; - {{- else if eq .Return.KindType "enum"}} + {{- if or (.Return.IsPrimitive) (eq .Return.KindType "enum")}} + {{javaType "" .Return }} expectedResult = new {{javaElementType "" .Return }}[1]; + expectedResult[0] = {{javaTestValue "" .Return }}; + {{- else }} + {{javaElementType "" .Return }}[] expectedResult = new {{javaElementType "" .Return }}[1]; + expectedResult[0] = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaElementType "" .Return )}}(); + {{- end}} + {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; {{- else }} - {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; - //TODO fill fields + {{javaReturn "" .Return }} expectedResult = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaType "" .Return )}}(); {{- end }} - {{- end }} + {{- end }} + AtomicBoolean receivedResp = new AtomicBoolean(false); {{javaAsyncReturn "" .Return}} resFuture = testedClient.{{camel .Name}}Async({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}}test{{javaVar $p}}{{- end }}); diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 92bd9d8..7abfa6d 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -22,6 +22,7 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel .In import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Module.Name}}TestHelper; {{- range .Module.Enums }} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; @@ -333,18 +334,20 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- template "prepareTestValue" .}} {{- template "putTestDataIntoBundle" .}} {{- end}} + {{- if not .Return.IsVoid }} {{- if .Return.IsArray }} - {{javaElementType "" .Return}} elementForResult = {{javaTestValue "" .Return }}; - // todo fill if is struct - {{javaReturn "" .Return }} returnedValue = new {{javaReturn "" .Return }}{elementForResult} ; - {{- else if (.Return.IsPrimitive) }} - {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; - {{- else if eq .Return.KindType "enum"}} + {{- if or (.Return.IsPrimitive) (eq .Return.KindType "enum")}} + {{javaType "" .Return }} returnedValue = new {{javaElementType "" .Return }}[1]; + returnedValue[0] = {{javaTestValue "" .Return }}; + {{- else }} + {{javaElementType "" .Return }}[] returnedValue = new {{javaElementType "" .Return }}[1]; + returnedValue[0] = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaElementType "" .Return )}}(); + {{- end}} + {{- else if or (.Return.IsPrimitive) (eq .Return.KindType "enum") }} {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; {{- else }} - {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; - //TODO fill fields + {{javaReturn "" .Return }} returnedValue = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaType "" .Return )}}(); {{- end }} diff --git a/templates/api/testhelper.java.tpl b/templates/api/testhelper.java.tpl new file mode 100644 index 0000000..51d673e --- /dev/null +++ b/templates/api/testhelper.java.tpl @@ -0,0 +1,34 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class {{Camel .Module.Name}}TestHelper +{ +{{- range .Module.Structs}} + + static public {{Camel .Name}} makeTest{{Camel .Name}}() + { + {{Camel .Name}} testStruct = new {{Camel .Name}}(); +{{- range .Fields }} +{{- if .IsArray}} + testStruct.{{camel .Name}} = new {{javaElementType "" . }}[1]; + {{- if or (.IsPrimitive) (eq .KindType "enum") }} + testStruct.{{camel .Name}}[0] = {{ javaTestValue "" . }}; + {{- else }} + testStruct.{{camel .Name}}[0] = makeTest{{Camel (javaElementType "" .) }}(); + {{- end }} +{{- else if or (.IsPrimitive) (eq .KindType "enum") }} + testStruct.{{camel .Name}} = {{javaTestValue "" . }}; +{{- else }} + testStruct.{{camel .Name}} = makeTest{{javaType "" . }}(); +{{- end}} +{{- end }} + return testStruct; + } + +{{- end}} + +} \ No newline at end of file diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index d61d197..610a8fe 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -22,6 +22,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; +import java.util.Arrays; public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface.Name}} { @@ -43,8 +44,14 @@ public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface @Override public void set{{Camel .Name}}({{javaParam "" .}}) { - Log.i(TAG, "request set{{Camel .Name}} callede "); + Log.i(TAG, "request set{{Camel .Name}} called "); + {{- if .IsArray }} + if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) + {{- else if or .IsPrimitive (eq .KindType "enum") }} if (m_{{javaVar .}} != {{javaVar .}}) + {{- else }} + if (! m_{{javaVar .}}.equals({{javaVar .}})) + {{- end}} { m_{{javaVar .}} = {{javaVar .}}; on{{Camel .Name}}Changed(m_{{javaVar .}}); From 82c0b5c757df74aafe665a117f71625a009e66ae Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 8 Sep 2025 13:15:09 +0200 Subject: [PATCH 31/45] feat: add example app Adds an app that uses composite build and builds all dependant modules. The modules are in the diretory of rootproject. Each module must provide a version. The app just makes instances of all intefaces implementations: stub and if android feature enabled also the client and service versions. Adds a task to run all unit tests. --- rules.yaml | 32 +++++++++++++++++ templates/android/client/build.gradle.tpl | 3 ++ templates/android/messenger/build.gradle.tpl | 3 ++ templates/android/service/build.gradle.tpl | 4 +++ templates/api/build.gradle.tpl | 3 ++ templates/example/AndroidManifest.xml.tpl | 16 +++++++++ templates/example/build.gradle.tpl | 33 +++++++++++++++++ templates/example/gradle.properties | 21 +++++++++++ templates/example/libs.versions.toml | 27 ++++++++++++++ templates/example/mainactivity.java.tpl | 35 ++++++++++++++++++ templates/example/rootbuild.gradle.tpl | 33 +++++++++++++++++ templates/example/settings.gradle.tpl | 36 +++++++++++++++++++ templates/stub/build.gradle.tpl | 3 ++ .../res/values/stringswholebuild.xml.tpl | 3 ++ 14 files changed, 252 insertions(+) create mode 100644 templates/example/AndroidManifest.xml.tpl create mode 100644 templates/example/build.gradle.tpl create mode 100644 templates/example/gradle.properties create mode 100644 templates/example/libs.versions.toml create mode 100644 templates/example/mainactivity.java.tpl create mode 100644 templates/example/rootbuild.gradle.tpl create mode 100644 templates/example/settings.gradle.tpl create mode 100644 templates/testappres/res/values/stringswholebuild.xml.tpl diff --git a/rules.yaml b/rules.yaml index 5a1296d..9e3bbf3 100644 --- a/rules.yaml +++ b/rules.yaml @@ -295,3 +295,35 @@ features: - source: "testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp" target: "res/mipmap-xxxhdpi/ic_launcher_round.webp" raw: true + + - name: example + requires: + - api + - stubs + scopes: + - match: system + documents: + - source: "example/rootbuild.gradle.tpl" + target: "build.gradle" + - source: "example/settings.gradle.tpl" + target: "settings.gradle" + - source: "example/gradle.properties" + target: "gradle.properties" + raw: true + - source: "libs.versions.toml" + target: "gradle/libs.versions.toml" + raw: true + - match: system + prefix: "{{camel .System.Name }}_example/" + documents: + - source: "example/build.gradle.tpl" + target: "build.gradle" + - match: system + prefix: "{{camel .System.Name }}_example/src/main/" + documents: + - source: "example/AndroidManifest.xml.tpl" + target: "AndroidManifest.xml" + - source: "example/mainactivity.java.tpl" + target: "java/{{camel .System.Name }}_example/{{Camel .System.Name }}MainActivity.java" + - source: "testappres/res/values/stringswholebuild.xml.tpl" + target: "res/values/strings.xml" \ No newline at end of file diff --git a/templates/android/client/build.gradle.tpl b/templates/android/client/build.gradle.tpl index 233c3f6..800115c 100644 --- a/templates/android/client/build.gradle.tpl +++ b/templates/android/client/build.gradle.tpl @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.library) } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_client' compileSdk 35 diff --git a/templates/android/messenger/build.gradle.tpl b/templates/android/messenger/build.gradle.tpl index c25b3f2..b1c8be4 100644 --- a/templates/android/messenger/build.gradle.tpl +++ b/templates/android/messenger/build.gradle.tpl @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.library) } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger' compileSdk 35 diff --git a/templates/android/service/build.gradle.tpl b/templates/android/service/build.gradle.tpl index cb57413..2e27bec 100644 --- a/templates/android/service/build.gradle.tpl +++ b/templates/android/service/build.gradle.tpl @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.library) } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_service' compileSdk 35 diff --git a/templates/api/build.gradle.tpl b/templates/api/build.gradle.tpl index 684828d..aa9593e 100644 --- a/templates/api/build.gradle.tpl +++ b/templates/api/build.gradle.tpl @@ -2,6 +2,9 @@ plugins { id 'java-library' } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 diff --git a/templates/example/AndroidManifest.xml.tpl b/templates/example/AndroidManifest.xml.tpl new file mode 100644 index 0000000..e878e67 --- /dev/null +++ b/templates/example/AndroidManifest.xml.tpl @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/example/build.gradle.tpl b/templates/example/build.gradle.tpl new file mode 100644 index 0000000..13385c5 --- /dev/null +++ b/templates/example/build.gradle.tpl @@ -0,0 +1,33 @@ +plugins { + alias(libs.plugins.android.library) +} +android { + namespace '{{camel .System.Name }}_example' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + {{- $withAndroid := .Features.android }} + + {{- range .System.Modules}} + {{- if $withAndroid }} + implementation("{{camel .Name}}:{{camel .Name}}_android_service:{{.Version}}") + implementation("{{camel .Name}}:{{camel .Name}}_android_client:{{.Version}}") + implementation("{{camel .Name}}:{{camel .Name}}_android_messenger:{{.Version}}") + {{- end }} + implementation("{{camel .Name}}:{{camel .Name}}_impl:{{.Version}}") + implementation("{{camel .Name}}:{{camel .Name}}_api:{{.Version}}") + {{- end }} + +} \ No newline at end of file diff --git a/templates/example/gradle.properties b/templates/example/gradle.properties new file mode 100644 index 0000000..e3bfeab --- /dev/null +++ b/templates/example/gradle.properties @@ -0,0 +1,21 @@ + # Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/templates/example/libs.versions.toml b/templates/example/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/templates/example/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/templates/example/mainactivity.java.tpl b/templates/example/mainactivity.java.tpl new file mode 100644 index 0000000..bed300f --- /dev/null +++ b/templates/example/mainactivity.java.tpl @@ -0,0 +1,35 @@ +package {{camel .System.Name }}_example; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +public class {{Camel .System.Name }}MainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + StringBuilder sb = new StringBuilder(); + {{- $withAndorid := .Features.android }} + + {{- range .System.Modules}} + {{$moduleName := camel .Name}} + {{- range .Interfaces}} + {{- if $withAndorid}} + {{$moduleName}}.{{$moduleName}}_android_client.{{Camel .Name}}Client {{$moduleName}}_{{camel .Name}}_client = new {{$moduleName}}.{{$moduleName}}_android_client.{{Camel .Name}}Client(this.getApplicationContext(), "conn_{{$moduleName}}_{{camel .Name}}_client"); + sb.append("Made instance of {{$moduleName}}.{{$moduleName}}_android_client.{{Camel .Name}}Client\n"); + {{$moduleName}}.{{$moduleName}}_android_service.{{Camel .Name}}ServiceAdapter {{$moduleName}}_{{camel .Name}}_service = new {{$moduleName}}.{{$moduleName}}_android_service.{{Camel .Name}}ServiceAdapter(); + sb.append("Made instance of {{$moduleName}}.{{$moduleName}}_android_service.{{Camel .Name}}ServiceAdapter\n"); + {{- end}} + {{$moduleName}}.{{$moduleName}}_impl.{{Camel .Name}}Service {{$moduleName}}_{{camel .Name}}_local_impl = new {{$moduleName}}.{{$moduleName}}_impl.{{Camel .Name}}Service(); + sb.append("Made instance of {{$moduleName}}.{{$moduleName}}_impl.{{Camel .Name}}Service\n"); + {{- end }} + {{- end }} + + // Show output on screen + TextView tv = new TextView(this); + tv.setText(sb.toString() + "\nPress Back to exit"); + setContentView(tv); + + } +} \ No newline at end of file diff --git a/templates/example/rootbuild.gradle.tpl b/templates/example/rootbuild.gradle.tpl new file mode 100644 index 0000000..ed7fcaf --- /dev/null +++ b/templates/example/rootbuild.gradle.tpl @@ -0,0 +1,33 @@ +buildscript { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + +} + +tasks.register('runAll') { + + description = "Builds app and all included builds (modulex, moduley) with all submodules" + group = "build" + {{- $withAndroid := .Features.android }} + + {{- range .System.Modules}} + {{- if $withAndroid }} + dependsOn gradle.includedBuild('{{camel .Name}}').task(':{{camel .Name}}_android_service:build') + dependsOn gradle.includedBuild('{{camel .Name}}').task(':{{camel .Name}}_android_client:build') + dependsOn gradle.includedBuild('{{camel .Name}}').task(':{{camel .Name}}_android_messenger:build') + {{- end }} + dependsOn gradle.includedBuild('{{camel .Name}}').task(':{{camel .Name}}_impl:build') + dependsOn gradle.includedBuild('{{camel .Name}}').task(':{{camel .Name}}_api:build') + {{- end }} + + dependsOn project(':{{camel .System.Name }}_example').tasks.named('build') + +} + +tasks.register("runJavaUnitTests") { + description = "Runs all tests in all submodules" + dependsOn subprojects.collect { it.tasks.withType(Test) } +} \ No newline at end of file diff --git a/templates/example/settings.gradle.tpl b/templates/example/settings.gradle.tpl new file mode 100644 index 0000000..3a5c894 --- /dev/null +++ b/templates/example/settings.gradle.tpl @@ -0,0 +1,36 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + includeGroupByRegex("org\\.jetbrains.*") // allow Kotlin + } + } + mavenCentral() + gradlePluginPortal() + } +} + + +rootProject.name = "{{camel .System.Name}}" +include ':{{camel .System.Name }}_example' +{{- $withAndroid := .Features.android }} +{{- range .System.Modules}} +includeBuild '{{camel .Name}}' +{{- end }} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + // your libs.versions.toml will be picked up automatically + } + } + +} diff --git a/templates/stub/build.gradle.tpl b/templates/stub/build.gradle.tpl index ce0196e..1a96221 100644 --- a/templates/stub/build.gradle.tpl +++ b/templates/stub/build.gradle.tpl @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.library) } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_impl' compileSdk 35 diff --git a/templates/testappres/res/values/stringswholebuild.xml.tpl b/templates/testappres/res/values/stringswholebuild.xml.tpl new file mode 100644 index 0000000..149bc0c --- /dev/null +++ b/templates/testappres/res/values/stringswholebuild.xml.tpl @@ -0,0 +1,3 @@ + + {{Camel .System.Name }}MainActivity + \ No newline at end of file From decec7139d7ff60d33130c28d700ae18767466d6 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:19:38 +0200 Subject: [PATCH 32/45] add goldenmaster --- .gitignore | 2 +- apigear/goldenmaster.solution.yaml | 2 +- goldenmaster/build.gradle | 57 + .../goldenmaster_example/build.gradle | 57 + .../src/main/AndroidManifest.xml | 16 + .../GoldenmasterMainActivity.java | 165 +++ .../src/main/res/values/strings.xml | 3 + goldenmaster/gradle.properties | 21 + goldenmaster/gradle/libs.versions.toml | 27 + goldenmaster/settings.gradle | 39 + goldenmaster/tb.enum.api/TbEnum.java | 152 -- goldenmaster/tb.names.api/TbNames.java | 90 -- goldenmaster/tb.same1.api/TbSame1.java | 269 ---- goldenmaster/tb.same2.api/TbSame2.java | 269 ---- goldenmaster/tb.simple.api/TbSimple.java | 616 -------- goldenmaster/tbEnum/gradle.properties | 21 + goldenmaster/tbEnum/gradle/libs.versions.toml | 27 + goldenmaster/tbEnum/settings.gradle | 29 + .../tbEnum_android_client/additions.gradle | 19 + .../tbEnum/tbEnum_android_client/build.gradle | 35 + .../EnumInterfaceClient.java | 717 ++++++++++ .../EnumInterfaceClientTest.java | 542 +++++++ .../tbEnum_android_messenger/additions.gradle | 18 + .../tbEnum_android_messenger/build.gradle | 30 + .../Enum0Parcelable.java | 69 + .../Enum1Parcelable.java | 69 + .../Enum2Parcelable.java | 69 + .../Enum3Parcelable.java | 69 + .../EnumInterfaceMessageType.java | 50 + .../tbEnum_android_service/additions.gradle | 20 + .../tbEnum_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 19 + .../EnumInterfaceServiceAdapter.java | 492 +++++++ .../EnumInterfaceServiceFactory.java | 81 ++ .../EnumInterfaceServiceStarter.java | 42 + .../IEnumInterfaceServiceFactory.java | 7 + .../EnumInterfaceServiceAdapterTest.java | 577 ++++++++ .../tbEnum/tbEnum_api/additions.gradle | 7 + goldenmaster/tbEnum/tbEnum_api/build.gradle | 15 + .../tbEnum_api/AbstractEnumInterface.java | 85 ++ .../main/java/tbEnum/tbEnum_api/Enum0.java | 30 + .../main/java/tbEnum/tbEnum_api/Enum1.java | 30 + .../main/java/tbEnum/tbEnum_api/Enum2.java | 30 + .../main/java/tbEnum/tbEnum_api/Enum3.java | 30 + .../tbEnum/tbEnum_api/IEnumInterface.java | 48 + .../IEnumInterfaceEventListener.java | 17 + .../tbEnum/tbEnum_api/TbEnumTestHelper.java | 11 + .../tbEnum/tbEnum_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../TbEnumTestClientApp.java | 358 +++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbEnum/tbEnum_impl/additions.gradle | 18 + goldenmaster/tbEnum/tbEnum_impl/build.gradle | 30 + .../tbEnum_impl/EnumInterfaceService.java | 221 +++ .../EnumInterfaceJniClient.java | 256 ++++ .../EnumInterfaceJniService.java | 221 +++ .../EnumInterfaceJniServiceFactory.java | 81 ++ .../EnumInterfaceJniServiceStarter.java | 42 + .../tbEnum/tbEnumserviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 29 + .../TbEnumTestServiceApp.java | 312 +++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/tbNames/gradle.properties | 21 + .../tbNames/gradle/libs.versions.toml | 27 + goldenmaster/tbNames/settings.gradle | 29 + .../tbNames_android_client/additions.gradle | 19 + .../tbNames_android_client/build.gradle | 35 + .../tbNames_android_client/NamEsClient.java | 497 +++++++ .../NamEsClientTest.java | 365 +++++ .../additions.gradle | 18 + .../tbNames_android_messenger/build.gradle | 30 + .../NamEsMessageType.java | 42 + .../tbNames_android_service/additions.gradle | 20 + .../tbNames_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 19 + .../INamEsServiceFactory.java | 7 + .../NamEsServiceAdapter.java | 371 +++++ .../NamEsServiceFactory.java | 81 ++ .../NamEsServiceStarter.java | 42 + .../NamEsServiceAdapterTest.java | 402 ++++++ .../tbNames/tbNames_api/additions.gradle | 7 + goldenmaster/tbNames/tbNames_api/build.gradle | 15 + .../tbNames/tbNames_api/AbstractNamEs.java | 60 + .../main/java/tbNames/tbNames_api/INamEs.java | 34 + .../tbNames_api/INamEsEventListener.java | 10 + .../tbNames_api/TbNamesTestHelper.java | 11 + .../tbNames_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../TbNamesTestClientApp.java | 288 ++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbNames/tbNames_impl/additions.gradle | 18 + .../tbNames/tbNames_impl/build.gradle | 30 + .../tbNames/tbNames_impl/NamEsService.java | 155 +++ .../tbNamesjniclient/NamEsJniClient.java | 180 +++ .../tbNamesjniservice/NamEsJniService.java | 156 +++ .../NamEsJniServiceFactory.java | 81 ++ .../NamEsJniServiceStarter.java | 42 + .../tbNamesserviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 29 + .../TbNamesTestServiceApp.java | 253 ++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/tbSame1/gradle.properties | 21 + .../tbSame1/gradle/libs.versions.toml | 27 + goldenmaster/tbSame1/settings.gradle | 29 + .../tbSame1_android_client/additions.gradle | 19 + .../tbSame1_android_client/build.gradle | 35 + .../SameEnum1InterfaceClient.java | 339 +++++ .../SameEnum2InterfaceClient.java | 471 +++++++ .../SameStruct1InterfaceClient.java | 339 +++++ .../SameStruct2InterfaceClient.java | 471 +++++++ .../SameEnum1InterfaceClientTest.java | 251 ++++ .../SameEnum2InterfaceClientTest.java | 355 +++++ .../SameStruct1InterfaceClientTest.java | 251 ++++ .../SameStruct2InterfaceClientTest.java | 355 +++++ .../additions.gradle | 18 + .../tbSame1_android_messenger/build.gradle | 30 + .../Enum1Parcelable.java | 69 + .../Enum2Parcelable.java | 69 + .../SameEnum1InterfaceMessageType.java | 35 + .../SameEnum2InterfaceMessageType.java | 40 + .../SameStruct1InterfaceMessageType.java | 35 + .../SameStruct2InterfaceMessageType.java | 40 + .../Struct1Parcelable.java | 69 + .../Struct2Parcelable.java | 69 + .../tbSame1_android_service/additions.gradle | 20 + .../tbSame1_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 31 + .../ISameEnum1InterfaceServiceFactory.java | 7 + .../ISameEnum2InterfaceServiceFactory.java | 7 + .../ISameStruct1InterfaceServiceFactory.java | 7 + .../ISameStruct2InterfaceServiceFactory.java | 7 + .../SameEnum1InterfaceServiceAdapter.java | 300 ++++ .../SameEnum1InterfaceServiceFactory.java | 81 ++ .../SameEnum1InterfaceServiceStarter.java | 42 + .../SameEnum2InterfaceServiceAdapter.java | 369 +++++ .../SameEnum2InterfaceServiceFactory.java | 81 ++ .../SameEnum2InterfaceServiceStarter.java | 42 + .../SameStruct1InterfaceServiceAdapter.java | 300 ++++ .../SameStruct1InterfaceServiceFactory.java | 81 ++ .../SameStruct1InterfaceServiceStarter.java | 42 + .../SameStruct2InterfaceServiceAdapter.java | 369 +++++ .../SameStruct2InterfaceServiceFactory.java | 81 ++ .../SameStruct2InterfaceServiceStarter.java | 42 + .../SameEnum1InterfaceServiceAdapterTest.java | 295 ++++ .../SameEnum2InterfaceServiceAdapterTest.java | 396 ++++++ ...ameStruct1InterfaceServiceAdapterTest.java | 296 ++++ ...ameStruct2InterfaceServiceAdapterTest.java | 398 ++++++ .../tbSame1/tbSame1_api/additions.gradle | 7 + goldenmaster/tbSame1/tbSame1_api/build.gradle | 15 + .../AbstractSameEnum1Interface.java | 43 + .../AbstractSameEnum2Interface.java | 57 + .../AbstractSameStruct1Interface.java | 43 + .../AbstractSameStruct2Interface.java | 57 + .../main/java/tbSame1/tbSame1_api/Enum1.java | 28 + .../main/java/tbSame1/tbSame1_api/Enum2.java | 28 + .../tbSame1_api/ISameEnum1Interface.java | 27 + .../ISameEnum1InterfaceEventListener.java | 11 + .../tbSame1_api/ISameEnum2Interface.java | 34 + .../ISameEnum2InterfaceEventListener.java | 13 + .../tbSame1_api/ISameStruct1Interface.java | 27 + .../ISameStruct1InterfaceEventListener.java | 11 + .../tbSame1_api/ISameStruct2Interface.java | 34 + .../ISameStruct2InterfaceEventListener.java | 13 + .../java/tbSame1/tbSame1_api/Struct1.java | 55 + .../java/tbSame1/tbSame1_api/Struct2.java | 55 + .../tbSame1_api/TbSame1TestHelper.java | 29 + .../tbSame1_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../TbSame1TestClientApp.java | 237 ++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbSame1/tbSame1_impl/additions.gradle | 18 + .../tbSame1/tbSame1_impl/build.gradle | 30 + .../SameEnum1InterfaceService.java | 89 ++ .../SameEnum2InterfaceService.java | 133 ++ .../SameStruct1InterfaceService.java | 89 ++ .../SameStruct2InterfaceService.java | 133 ++ .../SameEnum1InterfaceJniClient.java | 118 ++ .../SameEnum2InterfaceJniClient.java | 164 +++ .../SameStruct1InterfaceJniClient.java | 118 ++ .../SameStruct2InterfaceJniClient.java | 164 +++ .../SameEnum1InterfaceJniService.java | 95 ++ .../SameEnum1InterfaceJniServiceFactory.java | 81 ++ .../SameEnum1InterfaceJniServiceStarter.java | 42 + .../SameEnum2InterfaceJniService.java | 137 ++ .../SameEnum2InterfaceJniServiceFactory.java | 81 ++ .../SameEnum2InterfaceJniServiceStarter.java | 42 + .../SameStruct1InterfaceJniService.java | 95 ++ ...SameStruct1InterfaceJniServiceFactory.java | 81 ++ ...SameStruct1InterfaceJniServiceStarter.java | 42 + .../SameStruct2InterfaceJniService.java | 137 ++ ...SameStruct2InterfaceJniServiceFactory.java | 81 ++ ...SameStruct2InterfaceJniServiceStarter.java | 42 + .../tbSame1serviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 41 + .../TbSame1TestServiceApp.java | 210 +++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/tbSame2/gradle.properties | 21 + .../tbSame2/gradle/libs.versions.toml | 27 + goldenmaster/tbSame2/settings.gradle | 29 + .../tbSame2_android_client/additions.gradle | 19 + .../tbSame2_android_client/build.gradle | 35 + .../SameEnum1InterfaceClient.java | 339 +++++ .../SameEnum2InterfaceClient.java | 471 +++++++ .../SameStruct1InterfaceClient.java | 339 +++++ .../SameStruct2InterfaceClient.java | 471 +++++++ .../SameEnum1InterfaceClientTest.java | 251 ++++ .../SameEnum2InterfaceClientTest.java | 355 +++++ .../SameStruct1InterfaceClientTest.java | 251 ++++ .../SameStruct2InterfaceClientTest.java | 355 +++++ .../additions.gradle | 18 + .../tbSame2_android_messenger/build.gradle | 30 + .../Enum1Parcelable.java | 69 + .../Enum2Parcelable.java | 69 + .../SameEnum1InterfaceMessageType.java | 35 + .../SameEnum2InterfaceMessageType.java | 40 + .../SameStruct1InterfaceMessageType.java | 35 + .../SameStruct2InterfaceMessageType.java | 40 + .../Struct1Parcelable.java | 69 + .../Struct2Parcelable.java | 69 + .../tbSame2_android_service/additions.gradle | 20 + .../tbSame2_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 31 + .../ISameEnum1InterfaceServiceFactory.java | 7 + .../ISameEnum2InterfaceServiceFactory.java | 7 + .../ISameStruct1InterfaceServiceFactory.java | 7 + .../ISameStruct2InterfaceServiceFactory.java | 7 + .../SameEnum1InterfaceServiceAdapter.java | 300 ++++ .../SameEnum1InterfaceServiceFactory.java | 81 ++ .../SameEnum1InterfaceServiceStarter.java | 42 + .../SameEnum2InterfaceServiceAdapter.java | 369 +++++ .../SameEnum2InterfaceServiceFactory.java | 81 ++ .../SameEnum2InterfaceServiceStarter.java | 42 + .../SameStruct1InterfaceServiceAdapter.java | 300 ++++ .../SameStruct1InterfaceServiceFactory.java | 81 ++ .../SameStruct1InterfaceServiceStarter.java | 42 + .../SameStruct2InterfaceServiceAdapter.java | 369 +++++ .../SameStruct2InterfaceServiceFactory.java | 81 ++ .../SameStruct2InterfaceServiceStarter.java | 42 + .../SameEnum1InterfaceServiceAdapterTest.java | 295 ++++ .../SameEnum2InterfaceServiceAdapterTest.java | 396 ++++++ ...ameStruct1InterfaceServiceAdapterTest.java | 296 ++++ ...ameStruct2InterfaceServiceAdapterTest.java | 398 ++++++ .../tbSame2/tbSame2_api/additions.gradle | 7 + goldenmaster/tbSame2/tbSame2_api/build.gradle | 15 + .../AbstractSameEnum1Interface.java | 43 + .../AbstractSameEnum2Interface.java | 57 + .../AbstractSameStruct1Interface.java | 43 + .../AbstractSameStruct2Interface.java | 57 + .../main/java/tbSame2/tbSame2_api/Enum1.java | 28 + .../main/java/tbSame2/tbSame2_api/Enum2.java | 28 + .../tbSame2_api/ISameEnum1Interface.java | 27 + .../ISameEnum1InterfaceEventListener.java | 11 + .../tbSame2_api/ISameEnum2Interface.java | 34 + .../ISameEnum2InterfaceEventListener.java | 13 + .../tbSame2_api/ISameStruct1Interface.java | 27 + .../ISameStruct1InterfaceEventListener.java | 11 + .../tbSame2_api/ISameStruct2Interface.java | 34 + .../ISameStruct2InterfaceEventListener.java | 13 + .../java/tbSame2/tbSame2_api/Struct1.java | 55 + .../java/tbSame2/tbSame2_api/Struct2.java | 55 + .../tbSame2_api/TbSame2TestHelper.java | 29 + .../tbSame2_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../TbSame2TestClientApp.java | 237 ++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbSame2/tbSame2_impl/additions.gradle | 18 + .../tbSame2/tbSame2_impl/build.gradle | 30 + .../SameEnum1InterfaceService.java | 89 ++ .../SameEnum2InterfaceService.java | 133 ++ .../SameStruct1InterfaceService.java | 89 ++ .../SameStruct2InterfaceService.java | 133 ++ .../SameEnum1InterfaceJniClient.java | 118 ++ .../SameEnum2InterfaceJniClient.java | 164 +++ .../SameStruct1InterfaceJniClient.java | 118 ++ .../SameStruct2InterfaceJniClient.java | 164 +++ .../SameEnum1InterfaceJniService.java | 95 ++ .../SameEnum1InterfaceJniServiceFactory.java | 81 ++ .../SameEnum1InterfaceJniServiceStarter.java | 42 + .../SameEnum2InterfaceJniService.java | 137 ++ .../SameEnum2InterfaceJniServiceFactory.java | 81 ++ .../SameEnum2InterfaceJniServiceStarter.java | 42 + .../SameStruct1InterfaceJniService.java | 95 ++ ...SameStruct1InterfaceJniServiceFactory.java | 81 ++ ...SameStruct1InterfaceJniServiceStarter.java | 42 + .../SameStruct2InterfaceJniService.java | 137 ++ ...SameStruct2InterfaceJniServiceFactory.java | 81 ++ ...SameStruct2InterfaceJniServiceStarter.java | 42 + .../tbSame2serviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 41 + .../TbSame2TestServiceApp.java | 210 +++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/tbSimple/gradle.properties | 21 + .../tbSimple/gradle/libs.versions.toml | 27 + goldenmaster/tbSimple/settings.gradle | 29 + .../tbSimple_android_client/additions.gradle | 19 + .../tbSimple_android_client/build.gradle | 35 + .../EmptyInterfaceClient.java | 205 +++ .../NoOperationsInterfaceClient.java | 329 +++++ .../NoPropertiesInterfaceClient.java | 344 +++++ .../NoSignalsInterfaceClient.java | 420 ++++++ .../SimpleArrayInterfaceClient.java | 1231 ++++++++++++++++ .../SimpleInterfaceClient.java | 1239 +++++++++++++++++ .../VoidInterfaceClient.java | 272 ++++ .../EmptyInterfaceClientTest.java | 146 ++ .../NoOperationsInterfaceClientTest.java | 246 ++++ .../NoPropertiesInterfaceClientTest.java | 257 ++++ .../NoSignalsInterfaceClientTest.java | 297 ++++ .../SimpleArrayInterfaceClientTest.java | 989 +++++++++++++ .../SimpleInterfaceClientTest.java | 947 +++++++++++++ .../VoidInterfaceClientTest.java | 197 +++ .../additions.gradle | 18 + .../tbSimple_android_messenger/build.gradle | 30 + .../EmptyInterfaceMessageType.java | 30 + .../NoOperationsInterfaceMessageType.java | 36 + .../NoPropertiesInterfaceMessageType.java | 36 + .../NoSignalsInterfaceMessageType.java | 38 + .../SimpleArrayInterfaceMessageType.java | 72 + .../SimpleInterfaceMessageType.java | 72 + .../VoidInterfaceMessageType.java | 33 + .../tbSimple_android_service/additions.gradle | 20 + .../tbSimple_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 43 + .../EmptyInterfaceServiceAdapter.java | 228 +++ .../EmptyInterfaceServiceFactory.java | 81 ++ .../EmptyInterfaceServiceStarter.java | 42 + .../IEmptyInterfaceServiceFactory.java | 7 + .../INoOperationsInterfaceServiceFactory.java | 7 + .../INoPropertiesInterfaceServiceFactory.java | 7 + .../INoSignalsInterfaceServiceFactory.java | 7 + .../ISimpleArrayInterfaceServiceFactory.java | 7 + .../ISimpleInterfaceServiceFactory.java | 7 + .../IVoidInterfaceServiceFactory.java | 7 + .../NoOperationsInterfaceServiceAdapter.java | 294 ++++ .../NoOperationsInterfaceServiceFactory.java | 81 ++ .../NoOperationsInterfaceServiceStarter.java | 42 + .../NoPropertiesInterfaceServiceAdapter.java | 300 ++++ .../NoPropertiesInterfaceServiceFactory.java | 81 ++ .../NoPropertiesInterfaceServiceStarter.java | 42 + .../NoSignalsInterfaceServiceAdapter.java | 326 +++++ .../NoSignalsInterfaceServiceFactory.java | 81 ++ .../NoSignalsInterfaceServiceStarter.java | 42 + .../SimpleArrayInterfaceServiceAdapter.java | 747 ++++++++++ .../SimpleArrayInterfaceServiceFactory.java | 81 ++ .../SimpleArrayInterfaceServiceStarter.java | 42 + .../SimpleInterfaceServiceAdapter.java | 750 ++++++++++ .../SimpleInterfaceServiceFactory.java | 81 ++ .../SimpleInterfaceServiceStarter.java | 42 + .../VoidInterfaceServiceAdapter.java | 261 ++++ .../VoidInterfaceServiceFactory.java | 81 ++ .../VoidInterfaceServiceStarter.java | 42 + .../EmptyInterfaceServiceAdapterTest.java | 198 +++ ...OperationsInterfaceServiceAdapterTest.java | 305 ++++ ...PropertiesInterfaceServiceAdapterTest.java | 280 ++++ .../NoSignalsInterfaceServiceAdapterTest.java | 332 +++++ ...impleArrayInterfaceServiceAdapterTest.java | 1018 ++++++++++++++ .../SimpleInterfaceServiceAdapterTest.java | 959 +++++++++++++ .../VoidInterfaceServiceAdapterTest.java | 230 +++ .../tbSimple/tbSimple_api/additions.gradle | 7 + .../tbSimple/tbSimple_api/build.gradle | 15 + .../tbSimple_api/AbstractEmptyInterface.java | 25 + .../AbstractNoOperationsInterface.java | 53 + .../AbstractNoPropertiesInterface.java | 39 + .../AbstractNoSignalsInterface.java | 39 + .../AbstractSimpleArrayInterface.java | 144 ++ .../tbSimple_api/AbstractSimpleInterface.java | 137 ++ .../tbSimple_api/AbstractVoidInterface.java | 32 + .../tbSimple_api/IEmptyInterface.java | 16 + .../IEmptyInterfaceEventListener.java | 5 + .../tbSimple_api/INoOperationsInterface.java | 26 + .../INoOperationsInterfaceEventListener.java | 9 + .../tbSimple_api/INoPropertiesInterface.java | 22 + .../INoPropertiesInterfaceEventListener.java | 7 + .../tbSimple_api/INoSignalsInterface.java | 28 + .../INoSignalsInterfaceEventListener.java | 7 + .../tbSimple_api/ISimpleArrayInterface.java | 76 + .../ISimpleArrayInterfaceEventListener.java | 22 + .../tbSimple_api/ISimpleInterface.java | 74 + .../ISimpleInterfaceEventListener.java | 21 + .../tbSimple/tbSimple_api/IVoidInterface.java | 19 + .../IVoidInterfaceEventListener.java | 6 + .../tbSimple_api/TbSimpleTestHelper.java | 11 + .../tbSimple_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../TbSimpleTestClientApp.java | 211 +++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbSimple/tbSimple_impl/additions.gradle | 18 + .../tbSimple/tbSimple_impl/build.gradle | 30 + .../tbSimple_impl/EmptyInterfaceService.java | 41 + .../NoOperationsInterfaceService.java | 103 ++ .../NoPropertiesInterfaceService.java | 77 + .../NoSignalsInterfaceService.java | 119 ++ .../SimpleArrayInterfaceService.java | 419 ++++++ .../tbSimple_impl/SimpleInterfaceService.java | 406 ++++++ .../tbSimple_impl/VoidInterfaceService.java | 59 + .../EmptyInterfaceJniClient.java | 68 + .../NoOperationsInterfaceJniClient.java | 122 ++ .../NoPropertiesInterfaceJniClient.java | 120 ++ .../NoSignalsInterfaceJniClient.java | 146 ++ .../SimpleArrayInterfaceJniClient.java | 456 ++++++ .../SimpleInterfaceJniClient.java | 455 ++++++ .../VoidInterfaceJniClient.java | 94 ++ .../EmptyInterfaceJniService.java | 49 + .../EmptyInterfaceJniServiceFactory.java | 81 ++ .../EmptyInterfaceJniServiceStarter.java | 42 + .../NoOperationsInterfaceJniService.java | 105 ++ ...oOperationsInterfaceJniServiceFactory.java | 81 ++ ...oOperationsInterfaceJniServiceStarter.java | 42 + .../NoPropertiesInterfaceJniService.java | 87 ++ ...oPropertiesInterfaceJniServiceFactory.java | 81 ++ ...oPropertiesInterfaceJniServiceStarter.java | 42 + .../NoSignalsInterfaceJniService.java | 123 ++ .../NoSignalsInterfaceJniServiceFactory.java | 81 ++ .../NoSignalsInterfaceJniServiceStarter.java | 42 + .../SimpleArrayInterfaceJniService.java | 408 ++++++ ...SimpleArrayInterfaceJniServiceFactory.java | 81 ++ ...SimpleArrayInterfaceJniServiceStarter.java | 42 + .../SimpleInterfaceJniService.java | 399 ++++++ .../SimpleInterfaceJniServiceFactory.java | 81 ++ .../SimpleInterfaceJniServiceStarter.java | 42 + .../VoidInterfaceJniService.java | 68 + .../VoidInterfaceJniServiceFactory.java | 81 ++ .../VoidInterfaceJniServiceStarter.java | 42 + .../tbSimpleserviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 53 + .../TbSimpleTestServiceApp.java | 184 +++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/testbed1.api/Testbed1.java | 255 ---- goldenmaster/testbed1/gradle.properties | 21 + .../testbed1/gradle/libs.versions.toml | 27 + goldenmaster/testbed1/settings.gradle | 29 + .../testbed1_android_client/additions.gradle | 19 + .../testbed1_android_client/build.gradle | 35 + .../StructArrayInterfaceClient.java | 717 ++++++++++ .../StructInterfaceClient.java | 717 ++++++++++ .../StructArrayInterfaceClientTest.java | 566 ++++++++ .../StructInterfaceClientTest.java | 542 +++++++ .../additions.gradle | 18 + .../testbed1_android_messenger/build.gradle | 30 + .../StructArrayInterfaceMessageType.java | 50 + .../StructBoolParcelable.java | 65 + .../StructFloatParcelable.java | 65 + .../StructIntParcelable.java | 65 + .../StructInterfaceMessageType.java | 50 + .../StructStringParcelable.java | 65 + .../testbed1_android_service/additions.gradle | 20 + .../testbed1_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 23 + .../IStructArrayInterfaceServiceFactory.java | 7 + .../IStructInterfaceServiceFactory.java | 7 + .../StructArrayInterfaceServiceAdapter.java | 492 +++++++ .../StructArrayInterfaceServiceFactory.java | 81 ++ .../StructArrayInterfaceServiceStarter.java | 42 + .../StructInterfaceServiceAdapter.java | 492 +++++++ .../StructInterfaceServiceFactory.java | 81 ++ .../StructInterfaceServiceStarter.java | 42 + ...tructArrayInterfaceServiceAdapterTest.java | 605 ++++++++ .../StructInterfaceServiceAdapterTest.java | 581 ++++++++ .../testbed1/testbed1_api/additions.gradle | 7 + .../testbed1/testbed1_api/build.gradle | 15 + .../AbstractStructArrayInterface.java | 85 ++ .../testbed1_api/AbstractStructInterface.java | 85 ++ .../testbed1_api/IStructArrayInterface.java | 48 + .../IStructArrayInterfaceEventListener.java | 17 + .../testbed1_api/IStructInterface.java | 48 + .../IStructInterfaceEventListener.java | 17 + .../testbed1/testbed1_api/StructBool.java | 43 + .../testbed1/testbed1_api/StructFloat.java | 43 + .../java/testbed1/testbed1_api/StructInt.java | 43 + .../testbed1/testbed1_api/StructString.java | 43 + .../testbed1_api/Testbed1TestHelper.java | 39 + .../testbed1_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../Testbed1TestClientApp.java | 354 +++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../testbed1/testbed1_impl/additions.gradle | 18 + .../testbed1/testbed1_impl/build.gradle | 30 + .../StructArrayInterfaceService.java | 221 +++ .../testbed1_impl/StructInterfaceService.java | 221 +++ .../StructArrayInterfaceJniClient.java | 256 ++++ .../StructInterfaceJniClient.java | 256 ++++ .../StructArrayInterfaceJniService.java | 221 +++ ...StructArrayInterfaceJniServiceFactory.java | 81 ++ ...StructArrayInterfaceJniServiceStarter.java | 42 + .../StructInterfaceJniService.java | 221 +++ .../StructInterfaceJniServiceFactory.java | 81 ++ .../StructInterfaceJniServiceStarter.java | 42 + .../testbed1serviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 33 + .../Testbed1TestServiceApp.java | 312 +++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/testbed2.api/Testbed2.java | 421 ------ goldenmaster/testbed2/gradle.properties | 21 + .../testbed2/gradle/libs.versions.toml | 27 + goldenmaster/testbed2/settings.gradle | 29 + .../testbed2_android_client/additions.gradle | 19 + .../testbed2_android_client/build.gradle | 35 + .../ManyParamInterfaceClient.java | 737 ++++++++++ .../NestedStruct1InterfaceClient.java | 351 +++++ .../NestedStruct2InterfaceClient.java | 483 +++++++ .../NestedStruct3InterfaceClient.java | 621 +++++++++ .../ManyParamInterfaceClientTest.java | 582 ++++++++ .../NestedStruct1InterfaceClientTest.java | 263 ++++ .../NestedStruct2InterfaceClientTest.java | 367 +++++ .../NestedStruct3InterfaceClientTest.java | 478 +++++++ .../additions.gradle | 18 + .../testbed2_android_messenger/build.gradle | 30 + .../Enum1Parcelable.java | 69 + .../Enum2Parcelable.java | 69 + .../Enum3Parcelable.java | 69 + .../ManyParamInterfaceMessageType.java | 50 + .../NestedStruct1InterfaceMessageType.java | 35 + .../NestedStruct1Parcelable.java | 67 + .../NestedStruct2InterfaceMessageType.java | 40 + .../NestedStruct2Parcelable.java | 71 + .../NestedStruct3InterfaceMessageType.java | 45 + .../NestedStruct3Parcelable.java | 83 ++ .../Struct1Parcelable.java | 65 + .../Struct2Parcelable.java | 67 + .../Struct3Parcelable.java | 69 + .../Struct4Parcelable.java | 71 + .../testbed2_android_service/additions.gradle | 20 + .../testbed2_android_service/build.gradle | 37 + .../src/main/AndroidManifest.xml | 31 + .../IManyParamInterfaceServiceFactory.java | 7 + ...INestedStruct1InterfaceServiceFactory.java | 7 + ...INestedStruct2InterfaceServiceFactory.java | 7 + ...INestedStruct3InterfaceServiceFactory.java | 7 + .../ManyParamInterfaceServiceAdapter.java | 520 +++++++ .../ManyParamInterfaceServiceFactory.java | 81 ++ .../ManyParamInterfaceServiceStarter.java | 42 + .../NestedStruct1InterfaceServiceAdapter.java | 312 +++++ .../NestedStruct1InterfaceServiceFactory.java | 81 ++ .../NestedStruct1InterfaceServiceStarter.java | 42 + .../NestedStruct2InterfaceServiceAdapter.java | 381 +++++ .../NestedStruct2InterfaceServiceFactory.java | 81 ++ .../NestedStruct2InterfaceServiceStarter.java | 42 + .../NestedStruct3InterfaceServiceAdapter.java | 455 ++++++ .../NestedStruct3InterfaceServiceFactory.java | 81 ++ .../NestedStruct3InterfaceServiceStarter.java | 42 + .../ManyParamInterfaceServiceAdapterTest.java | 613 ++++++++ ...tedStruct1InterfaceServiceAdapterTest.java | 308 ++++ ...tedStruct2InterfaceServiceAdapterTest.java | 410 ++++++ ...tedStruct3InterfaceServiceAdapterTest.java | 519 +++++++ .../testbed2/testbed2_api/additions.gradle | 7 + .../testbed2/testbed2_api/build.gradle | 15 + .../AbstractManyParamInterface.java | 91 ++ .../AbstractNestedStruct1Interface.java | 49 + .../AbstractNestedStruct2Interface.java | 63 + .../AbstractNestedStruct3Interface.java | 77 + .../java/testbed2/testbed2_api/Enum1.java | 32 + .../java/testbed2/testbed2_api/Enum2.java | 32 + .../java/testbed2/testbed2_api/Enum3.java | 32 + .../testbed2_api/IManyParamInterface.java | 54 + .../IManyParamInterfaceEventListener.java | 23 + .../testbed2_api/INestedStruct1Interface.java | 33 + .../INestedStruct1InterfaceEventListener.java | 17 + .../testbed2_api/INestedStruct2Interface.java | 40 + .../INestedStruct2InterfaceEventListener.java | 19 + .../testbed2_api/INestedStruct3Interface.java | 47 + .../INestedStruct3InterfaceEventListener.java | 21 + .../testbed2/testbed2_api/NestedStruct1.java | 44 + .../testbed2/testbed2_api/NestedStruct2.java | 51 + .../testbed2/testbed2_api/NestedStruct3.java | 87 ++ .../java/testbed2/testbed2_api/Struct1.java | 43 + .../java/testbed2/testbed2_api/Struct2.java | 49 + .../java/testbed2/testbed2_api/Struct3.java | 55 + .../java/testbed2/testbed2_api/Struct4.java | 61 + .../testbed2_api/Testbed2TestHelper.java | 75 + .../testbed2_client_example/build.gradle | 41 + .../src/main/AndroidManifest.xml | 27 + .../Testbed2TestClientApp.java | 376 +++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../testbed2/testbed2_impl/additions.gradle | 18 + .../testbed2/testbed2_impl/build.gradle | 30 + .../ManyParamInterfaceService.java | 227 +++ .../NestedStruct1InterfaceService.java | 95 ++ .../NestedStruct2InterfaceService.java | 139 ++ .../NestedStruct3InterfaceService.java | 183 +++ .../ManyParamInterfaceJniClient.java | 262 ++++ .../NestedStruct1InterfaceJniClient.java | 124 ++ .../NestedStruct2InterfaceJniClient.java | 170 +++ .../NestedStruct3InterfaceJniClient.java | 216 +++ .../ManyParamInterfaceJniService.java | 227 +++ .../ManyParamInterfaceJniServiceFactory.java | 81 ++ .../ManyParamInterfaceJniServiceStarter.java | 42 + .../NestedStruct1InterfaceJniService.java | 101 ++ ...stedStruct1InterfaceJniServiceFactory.java | 81 ++ ...stedStruct1InterfaceJniServiceStarter.java | 42 + .../NestedStruct2InterfaceJniService.java | 143 ++ ...stedStruct2InterfaceJniServiceFactory.java | 81 ++ ...stedStruct2InterfaceJniServiceStarter.java | 42 + .../NestedStruct3InterfaceJniService.java | 185 +++ ...stedStruct3InterfaceJniServiceFactory.java | 81 ++ ...stedStruct3InterfaceJniServiceStarter.java | 42 + .../testbed2serviceexample/build.gradle | 42 + .../src/main/AndroidManifest.xml | 41 + .../Testbed2TestServiceApp.java | 330 +++++ .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + 864 files changed, 76274 insertions(+), 2074 deletions(-) create mode 100644 goldenmaster/build.gradle create mode 100644 goldenmaster/goldenmaster_example/build.gradle create mode 100644 goldenmaster/goldenmaster_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java create mode 100644 goldenmaster/goldenmaster_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/gradle.properties create mode 100644 goldenmaster/gradle/libs.versions.toml create mode 100644 goldenmaster/settings.gradle delete mode 100644 goldenmaster/tb.enum.api/TbEnum.java delete mode 100644 goldenmaster/tb.names.api/TbNames.java delete mode 100644 goldenmaster/tb.same1.api/TbSame1.java delete mode 100644 goldenmaster/tb.same2.api/TbSame2.java delete mode 100644 goldenmaster/tb.simple.api/TbSimple.java create mode 100644 goldenmaster/tbEnum/gradle.properties create mode 100644 goldenmaster/tbEnum/gradle/libs.versions.toml create mode 100644 goldenmaster/tbEnum/settings.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_client/additions.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_client/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/additions.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum0Parcelable.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum1Parcelable.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum2Parcelable.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum3Parcelable.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceMessageType.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/additions.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/IEnumInterfaceServiceFactory.java create mode 100644 goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/additions.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_api/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum0.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum1.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum2.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum3.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterface.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterfaceEventListener.java create mode 100644 goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbEnum/tbEnum_impl/additions.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_impl/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java create mode 100644 goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java create mode 100644 goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java create mode 100644 goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/build.gradle create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbNames/gradle.properties create mode 100644 goldenmaster/tbNames/gradle/libs.versions.toml create mode 100644 goldenmaster/tbNames/settings.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_client/additions.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_client/build.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java create mode 100644 goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java create mode 100644 goldenmaster/tbNames/tbNames_android_messenger/additions.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_messenger/build.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java create mode 100644 goldenmaster/tbNames/tbNames_android_service/additions.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_service/build.gradle create mode 100644 goldenmaster/tbNames/tbNames_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/INamEsServiceFactory.java create mode 100644 goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java create mode 100644 goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java create mode 100644 goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java create mode 100644 goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java create mode 100644 goldenmaster/tbNames/tbNames_api/additions.gradle create mode 100644 goldenmaster/tbNames/tbNames_api/build.gradle create mode 100644 goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java create mode 100644 goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java create mode 100644 goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java create mode 100644 goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java create mode 100644 goldenmaster/tbNames/tbNames_client_example/build.gradle create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbNames/tbNames_impl/additions.gradle create mode 100644 goldenmaster/tbNames/tbNames_impl/build.gradle create mode 100644 goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java create mode 100644 goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java create mode 100644 goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java create mode 100644 goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java create mode 100644 goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/build.gradle create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSame1/gradle.properties create mode 100644 goldenmaster/tbSame1/gradle/libs.versions.toml create mode 100644 goldenmaster/tbSame1/settings.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/additions.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/additions.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum1Parcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum2Parcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceMessageType.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceMessageType.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceMessageType.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceMessageType.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct1Parcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct2Parcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/additions.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/additions.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_api/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum1.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum2.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1InterfaceEventListener.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2InterfaceEventListener.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1InterfaceEventListener.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2Interface.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2InterfaceEventListener.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct1.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct2.java create mode 100644 goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSame1/tbSame1_impl/additions.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_impl/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java create mode 100644 goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java create mode 100644 goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java create mode 100644 goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java create mode 100644 goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java create mode 100644 goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java create mode 100644 goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java create mode 100644 goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/build.gradle create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSame2/gradle.properties create mode 100644 goldenmaster/tbSame2/gradle/libs.versions.toml create mode 100644 goldenmaster/tbSame2/settings.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/additions.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/additions.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum1Parcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum2Parcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceMessageType.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceMessageType.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceMessageType.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceMessageType.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct1Parcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct2Parcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/additions.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/additions.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_api/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum1.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum2.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1InterfaceEventListener.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2InterfaceEventListener.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1InterfaceEventListener.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2Interface.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2InterfaceEventListener.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct1.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct2.java create mode 100644 goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSame2/tbSame2_impl/additions.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_impl/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java create mode 100644 goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java create mode 100644 goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java create mode 100644 goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java create mode 100644 goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java create mode 100644 goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java create mode 100644 goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java create mode 100644 goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/build.gradle create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSimple/gradle.properties create mode 100644 goldenmaster/tbSimple/gradle/libs.versions.toml create mode 100644 goldenmaster/tbSimple/settings.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/additions.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/additions.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceMessageType.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/additions.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IEmptyInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoOperationsInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoPropertiesInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoSignalsInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleArrayInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IVoidInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/additions.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_api/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterface.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterfaceEventListener.java create mode 100644 goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSimple/tbSimple_impl/additions.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_impl/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java create mode 100644 goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/data_extraction_rules.xml delete mode 100644 goldenmaster/testbed1.api/Testbed1.java create mode 100644 goldenmaster/testbed1/gradle.properties create mode 100644 goldenmaster/testbed1/gradle/libs.versions.toml create mode 100644 goldenmaster/testbed1/settings.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_client/additions.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_client/build.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java create mode 100644 goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java create mode 100644 goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java create mode 100644 goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/additions.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/build.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceMessageType.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/additions.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_service/build.gradle create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArrayInterfaceServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructInterfaceServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed1/testbed1_api/additions.gradle create mode 100644 goldenmaster/testbed1/testbed1_api/build.gradle create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBool.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloat.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructInt.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructString.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java create mode 100644 goldenmaster/testbed1/testbed1_client_example/build.gradle create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/testbed1/testbed1_impl/additions.gradle create mode 100644 goldenmaster/testbed1/testbed1_impl/build.gradle create mode 100644 goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java create mode 100644 goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java create mode 100644 goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java create mode 100644 goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed1/testbed1serviceexample/build.gradle create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/data_extraction_rules.xml delete mode 100644 goldenmaster/testbed2.api/Testbed2.java create mode 100644 goldenmaster/testbed2/gradle.properties create mode 100644 goldenmaster/testbed2/gradle/libs.versions.toml create mode 100644 goldenmaster/testbed2/settings.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_client/additions.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_client/build.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/additions.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/build.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum1Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum2Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum3Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceMessageType.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceMessageType.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceMessageType.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct1Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct2Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct3Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct4Parcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/additions.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_service/build.gradle create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/IManyParamInterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct1InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct2InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct3InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed2/testbed2_api/additions.gradle create mode 100644 goldenmaster/testbed2/testbed2_api/build.gradle create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum1.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum2.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum3.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterfaceEventListener.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1InterfaceEventListener.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2Interface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2InterfaceEventListener.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3Interface.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3InterfaceEventListener.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct1.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct2.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct1.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct2.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct3.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct4.java create mode 100644 goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java create mode 100644 goldenmaster/testbed2/testbed2_client_example/build.gradle create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/testbed2/testbed2_impl/additions.gradle create mode 100644 goldenmaster/testbed2/testbed2_impl/build.gradle create mode 100644 goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java create mode 100644 goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java create mode 100644 goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java create mode 100644 goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java create mode 100644 goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java create mode 100644 goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java create mode 100644 goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java create mode 100644 goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2serviceexample/build.gradle create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/data_extraction_rules.xml diff --git a/.gitignore b/.gitignore index 675d0f7..9e73984 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,6 @@ .DS_Store .vscode/ .idea/ -test/ +./test/ /demo/app/bin /demo/gradle diff --git a/apigear/goldenmaster.solution.yaml b/apigear/goldenmaster.solution.yaml index cc0d4cd..bbdafe8 100644 --- a/apigear/goldenmaster.solution.yaml +++ b/apigear/goldenmaster.solution.yaml @@ -14,7 +14,7 @@ targets: - ../test-apis/testbed.same2.module.yaml - ../test-apis/testbed.simple.module.yaml - ../test-apis/testbed.struct.module.yaml - output: ../test + output: ../test2 template: .. force: true features: ["all"] diff --git a/goldenmaster/build.gradle b/goldenmaster/build.gradle new file mode 100644 index 0000000..c5765ed --- /dev/null +++ b/goldenmaster/build.gradle @@ -0,0 +1,57 @@ +buildscript { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + +} + +tasks.register('runAll') { + + description = "Builds app and all included builds (modulex, moduley) with all submodules" + group = "build" + dependsOn gradle.includedBuild('testbed2').task(':testbed2_android_service:build') + dependsOn gradle.includedBuild('testbed2').task(':testbed2_android_client:build') + dependsOn gradle.includedBuild('testbed2').task(':testbed2_android_messenger:build') + dependsOn gradle.includedBuild('testbed2').task(':testbed2_impl:build') + dependsOn gradle.includedBuild('testbed2').task(':testbed2_api:build') + dependsOn gradle.includedBuild('tbEnum').task(':tbEnum_android_service:build') + dependsOn gradle.includedBuild('tbEnum').task(':tbEnum_android_client:build') + dependsOn gradle.includedBuild('tbEnum').task(':tbEnum_android_messenger:build') + dependsOn gradle.includedBuild('tbEnum').task(':tbEnum_impl:build') + dependsOn gradle.includedBuild('tbEnum').task(':tbEnum_api:build') + dependsOn gradle.includedBuild('tbNames').task(':tbNames_android_service:build') + dependsOn gradle.includedBuild('tbNames').task(':tbNames_android_client:build') + dependsOn gradle.includedBuild('tbNames').task(':tbNames_android_messenger:build') + dependsOn gradle.includedBuild('tbNames').task(':tbNames_impl:build') + dependsOn gradle.includedBuild('tbNames').task(':tbNames_api:build') + dependsOn gradle.includedBuild('tbSame1').task(':tbSame1_android_service:build') + dependsOn gradle.includedBuild('tbSame1').task(':tbSame1_android_client:build') + dependsOn gradle.includedBuild('tbSame1').task(':tbSame1_android_messenger:build') + dependsOn gradle.includedBuild('tbSame1').task(':tbSame1_impl:build') + dependsOn gradle.includedBuild('tbSame1').task(':tbSame1_api:build') + dependsOn gradle.includedBuild('tbSame2').task(':tbSame2_android_service:build') + dependsOn gradle.includedBuild('tbSame2').task(':tbSame2_android_client:build') + dependsOn gradle.includedBuild('tbSame2').task(':tbSame2_android_messenger:build') + dependsOn gradle.includedBuild('tbSame2').task(':tbSame2_impl:build') + dependsOn gradle.includedBuild('tbSame2').task(':tbSame2_api:build') + dependsOn gradle.includedBuild('tbSimple').task(':tbSimple_android_service:build') + dependsOn gradle.includedBuild('tbSimple').task(':tbSimple_android_client:build') + dependsOn gradle.includedBuild('tbSimple').task(':tbSimple_android_messenger:build') + dependsOn gradle.includedBuild('tbSimple').task(':tbSimple_impl:build') + dependsOn gradle.includedBuild('tbSimple').task(':tbSimple_api:build') + dependsOn gradle.includedBuild('testbed1').task(':testbed1_android_service:build') + dependsOn gradle.includedBuild('testbed1').task(':testbed1_android_client:build') + dependsOn gradle.includedBuild('testbed1').task(':testbed1_android_messenger:build') + dependsOn gradle.includedBuild('testbed1').task(':testbed1_impl:build') + dependsOn gradle.includedBuild('testbed1').task(':testbed1_api:build') + + dependsOn project(':goldenmaster_example').tasks.named('build') + +} + +tasks.register("runJavaUnitTests") { + description = "Runs all tests in all submodules" + dependsOn subprojects.collect { it.tasks.withType(Test) } +} \ No newline at end of file diff --git a/goldenmaster/goldenmaster_example/build.gradle b/goldenmaster/goldenmaster_example/build.gradle new file mode 100644 index 0000000..0296c5c --- /dev/null +++ b/goldenmaster/goldenmaster_example/build.gradle @@ -0,0 +1,57 @@ +plugins { + alias(libs.plugins.android.library) +} +android { + namespace 'goldenmaster_example' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation("testbed2:testbed2_android_service:1.0.0") + implementation("testbed2:testbed2_android_client:1.0.0") + implementation("testbed2:testbed2_android_messenger:1.0.0") + implementation("testbed2:testbed2_impl:1.0.0") + implementation("testbed2:testbed2_api:1.0.0") + implementation("tbEnum:tbEnum_android_service:1.0.0") + implementation("tbEnum:tbEnum_android_client:1.0.0") + implementation("tbEnum:tbEnum_android_messenger:1.0.0") + implementation("tbEnum:tbEnum_impl:1.0.0") + implementation("tbEnum:tbEnum_api:1.0.0") + implementation("tbNames:tbNames_android_service:1.0.0") + implementation("tbNames:tbNames_android_client:1.0.0") + implementation("tbNames:tbNames_android_messenger:1.0.0") + implementation("tbNames:tbNames_impl:1.0.0") + implementation("tbNames:tbNames_api:1.0.0") + implementation("tbSame1:tbSame1_android_service:1.0.0") + implementation("tbSame1:tbSame1_android_client:1.0.0") + implementation("tbSame1:tbSame1_android_messenger:1.0.0") + implementation("tbSame1:tbSame1_impl:1.0.0") + implementation("tbSame1:tbSame1_api:1.0.0") + implementation("tbSame2:tbSame2_android_service:1.0.0") + implementation("tbSame2:tbSame2_android_client:1.0.0") + implementation("tbSame2:tbSame2_android_messenger:1.0.0") + implementation("tbSame2:tbSame2_impl:1.0.0") + implementation("tbSame2:tbSame2_api:1.0.0") + implementation("tbSimple:tbSimple_android_service:1.0.0") + implementation("tbSimple:tbSimple_android_client:1.0.0") + implementation("tbSimple:tbSimple_android_messenger:1.0.0") + implementation("tbSimple:tbSimple_impl:1.0.0") + implementation("tbSimple:tbSimple_api:1.0.0") + implementation("testbed1:testbed1_android_service:1.0.0") + implementation("testbed1:testbed1_android_client:1.0.0") + implementation("testbed1:testbed1_android_messenger:1.0.0") + implementation("testbed1:testbed1_impl:1.0.0") + implementation("testbed1:testbed1_api:1.0.0") + +} \ No newline at end of file diff --git a/goldenmaster/goldenmaster_example/src/main/AndroidManifest.xml b/goldenmaster/goldenmaster_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7d48b05 --- /dev/null +++ b/goldenmaster/goldenmaster_example/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java b/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java new file mode 100644 index 0000000..0b5db5e --- /dev/null +++ b/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java @@ -0,0 +1,165 @@ +package goldenmaster_example; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +public class GoldenmasterMainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + StringBuilder sb = new StringBuilder(); + + testbed2.testbed2_android_client.ManyParamInterfaceClient testbed2_manyParamInterface_client = new testbed2.testbed2_android_client.ManyParamInterfaceClient(this.getApplicationContext(), "conn_testbed2_manyParamInterface_client"); + sb.append("Made instance of testbed2.testbed2_android_client.ManyParamInterfaceClient\n"); + testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter testbed2_manyParamInterface_service = new testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter(); + sb.append("Made instance of testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter\n"); + testbed2.testbed2_impl.ManyParamInterfaceService testbed2_manyParamInterface_local_impl = new testbed2.testbed2_impl.ManyParamInterfaceService(); + sb.append("Made instance of testbed2.testbed2_impl.ManyParamInterfaceService\n"); + testbed2.testbed2_android_client.NestedStruct1InterfaceClient testbed2_nestedStruct1Interface_client = new testbed2.testbed2_android_client.NestedStruct1InterfaceClient(this.getApplicationContext(), "conn_testbed2_nestedStruct1Interface_client"); + sb.append("Made instance of testbed2.testbed2_android_client.NestedStruct1InterfaceClient\n"); + testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter testbed2_nestedStruct1Interface_service = new testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter(); + sb.append("Made instance of testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter\n"); + testbed2.testbed2_impl.NestedStruct1InterfaceService testbed2_nestedStruct1Interface_local_impl = new testbed2.testbed2_impl.NestedStruct1InterfaceService(); + sb.append("Made instance of testbed2.testbed2_impl.NestedStruct1InterfaceService\n"); + testbed2.testbed2_android_client.NestedStruct2InterfaceClient testbed2_nestedStruct2Interface_client = new testbed2.testbed2_android_client.NestedStruct2InterfaceClient(this.getApplicationContext(), "conn_testbed2_nestedStruct2Interface_client"); + sb.append("Made instance of testbed2.testbed2_android_client.NestedStruct2InterfaceClient\n"); + testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter testbed2_nestedStruct2Interface_service = new testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter(); + sb.append("Made instance of testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter\n"); + testbed2.testbed2_impl.NestedStruct2InterfaceService testbed2_nestedStruct2Interface_local_impl = new testbed2.testbed2_impl.NestedStruct2InterfaceService(); + sb.append("Made instance of testbed2.testbed2_impl.NestedStruct2InterfaceService\n"); + testbed2.testbed2_android_client.NestedStruct3InterfaceClient testbed2_nestedStruct3Interface_client = new testbed2.testbed2_android_client.NestedStruct3InterfaceClient(this.getApplicationContext(), "conn_testbed2_nestedStruct3Interface_client"); + sb.append("Made instance of testbed2.testbed2_android_client.NestedStruct3InterfaceClient\n"); + testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter testbed2_nestedStruct3Interface_service = new testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter(); + sb.append("Made instance of testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter\n"); + testbed2.testbed2_impl.NestedStruct3InterfaceService testbed2_nestedStruct3Interface_local_impl = new testbed2.testbed2_impl.NestedStruct3InterfaceService(); + sb.append("Made instance of testbed2.testbed2_impl.NestedStruct3InterfaceService\n"); + + tbEnum.tbEnum_android_client.EnumInterfaceClient tbEnum_enumInterface_client = new tbEnum.tbEnum_android_client.EnumInterfaceClient(this.getApplicationContext(), "conn_tbEnum_enumInterface_client"); + sb.append("Made instance of tbEnum.tbEnum_android_client.EnumInterfaceClient\n"); + tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter tbEnum_enumInterface_service = new tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter(); + sb.append("Made instance of tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter\n"); + tbEnum.tbEnum_impl.EnumInterfaceService tbEnum_enumInterface_local_impl = new tbEnum.tbEnum_impl.EnumInterfaceService(); + sb.append("Made instance of tbEnum.tbEnum_impl.EnumInterfaceService\n"); + + tbNames.tbNames_android_client.NamEsClient tbNames_namEs_client = new tbNames.tbNames_android_client.NamEsClient(this.getApplicationContext(), "conn_tbNames_namEs_client"); + sb.append("Made instance of tbNames.tbNames_android_client.NamEsClient\n"); + tbNames.tbNames_android_service.NamEsServiceAdapter tbNames_namEs_service = new tbNames.tbNames_android_service.NamEsServiceAdapter(); + sb.append("Made instance of tbNames.tbNames_android_service.NamEsServiceAdapter\n"); + tbNames.tbNames_impl.NamEsService tbNames_namEs_local_impl = new tbNames.tbNames_impl.NamEsService(); + sb.append("Made instance of tbNames.tbNames_impl.NamEsService\n"); + + tbSame1.tbSame1_android_client.SameStruct1InterfaceClient tbSame1_sameStruct1Interface_client = new tbSame1.tbSame1_android_client.SameStruct1InterfaceClient(this.getApplicationContext(), "conn_tbSame1_sameStruct1Interface_client"); + sb.append("Made instance of tbSame1.tbSame1_android_client.SameStruct1InterfaceClient\n"); + tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter tbSame1_sameStruct1Interface_service = new tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter(); + sb.append("Made instance of tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter\n"); + tbSame1.tbSame1_impl.SameStruct1InterfaceService tbSame1_sameStruct1Interface_local_impl = new tbSame1.tbSame1_impl.SameStruct1InterfaceService(); + sb.append("Made instance of tbSame1.tbSame1_impl.SameStruct1InterfaceService\n"); + tbSame1.tbSame1_android_client.SameStruct2InterfaceClient tbSame1_sameStruct2Interface_client = new tbSame1.tbSame1_android_client.SameStruct2InterfaceClient(this.getApplicationContext(), "conn_tbSame1_sameStruct2Interface_client"); + sb.append("Made instance of tbSame1.tbSame1_android_client.SameStruct2InterfaceClient\n"); + tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter tbSame1_sameStruct2Interface_service = new tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter(); + sb.append("Made instance of tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter\n"); + tbSame1.tbSame1_impl.SameStruct2InterfaceService tbSame1_sameStruct2Interface_local_impl = new tbSame1.tbSame1_impl.SameStruct2InterfaceService(); + sb.append("Made instance of tbSame1.tbSame1_impl.SameStruct2InterfaceService\n"); + tbSame1.tbSame1_android_client.SameEnum1InterfaceClient tbSame1_sameEnum1Interface_client = new tbSame1.tbSame1_android_client.SameEnum1InterfaceClient(this.getApplicationContext(), "conn_tbSame1_sameEnum1Interface_client"); + sb.append("Made instance of tbSame1.tbSame1_android_client.SameEnum1InterfaceClient\n"); + tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter tbSame1_sameEnum1Interface_service = new tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter(); + sb.append("Made instance of tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter\n"); + tbSame1.tbSame1_impl.SameEnum1InterfaceService tbSame1_sameEnum1Interface_local_impl = new tbSame1.tbSame1_impl.SameEnum1InterfaceService(); + sb.append("Made instance of tbSame1.tbSame1_impl.SameEnum1InterfaceService\n"); + tbSame1.tbSame1_android_client.SameEnum2InterfaceClient tbSame1_sameEnum2Interface_client = new tbSame1.tbSame1_android_client.SameEnum2InterfaceClient(this.getApplicationContext(), "conn_tbSame1_sameEnum2Interface_client"); + sb.append("Made instance of tbSame1.tbSame1_android_client.SameEnum2InterfaceClient\n"); + tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter tbSame1_sameEnum2Interface_service = new tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter(); + sb.append("Made instance of tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter\n"); + tbSame1.tbSame1_impl.SameEnum2InterfaceService tbSame1_sameEnum2Interface_local_impl = new tbSame1.tbSame1_impl.SameEnum2InterfaceService(); + sb.append("Made instance of tbSame1.tbSame1_impl.SameEnum2InterfaceService\n"); + + tbSame2.tbSame2_android_client.SameStruct1InterfaceClient tbSame2_sameStruct1Interface_client = new tbSame2.tbSame2_android_client.SameStruct1InterfaceClient(this.getApplicationContext(), "conn_tbSame2_sameStruct1Interface_client"); + sb.append("Made instance of tbSame2.tbSame2_android_client.SameStruct1InterfaceClient\n"); + tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter tbSame2_sameStruct1Interface_service = new tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter(); + sb.append("Made instance of tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter\n"); + tbSame2.tbSame2_impl.SameStruct1InterfaceService tbSame2_sameStruct1Interface_local_impl = new tbSame2.tbSame2_impl.SameStruct1InterfaceService(); + sb.append("Made instance of tbSame2.tbSame2_impl.SameStruct1InterfaceService\n"); + tbSame2.tbSame2_android_client.SameStruct2InterfaceClient tbSame2_sameStruct2Interface_client = new tbSame2.tbSame2_android_client.SameStruct2InterfaceClient(this.getApplicationContext(), "conn_tbSame2_sameStruct2Interface_client"); + sb.append("Made instance of tbSame2.tbSame2_android_client.SameStruct2InterfaceClient\n"); + tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter tbSame2_sameStruct2Interface_service = new tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter(); + sb.append("Made instance of tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter\n"); + tbSame2.tbSame2_impl.SameStruct2InterfaceService tbSame2_sameStruct2Interface_local_impl = new tbSame2.tbSame2_impl.SameStruct2InterfaceService(); + sb.append("Made instance of tbSame2.tbSame2_impl.SameStruct2InterfaceService\n"); + tbSame2.tbSame2_android_client.SameEnum1InterfaceClient tbSame2_sameEnum1Interface_client = new tbSame2.tbSame2_android_client.SameEnum1InterfaceClient(this.getApplicationContext(), "conn_tbSame2_sameEnum1Interface_client"); + sb.append("Made instance of tbSame2.tbSame2_android_client.SameEnum1InterfaceClient\n"); + tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter tbSame2_sameEnum1Interface_service = new tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter(); + sb.append("Made instance of tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter\n"); + tbSame2.tbSame2_impl.SameEnum1InterfaceService tbSame2_sameEnum1Interface_local_impl = new tbSame2.tbSame2_impl.SameEnum1InterfaceService(); + sb.append("Made instance of tbSame2.tbSame2_impl.SameEnum1InterfaceService\n"); + tbSame2.tbSame2_android_client.SameEnum2InterfaceClient tbSame2_sameEnum2Interface_client = new tbSame2.tbSame2_android_client.SameEnum2InterfaceClient(this.getApplicationContext(), "conn_tbSame2_sameEnum2Interface_client"); + sb.append("Made instance of tbSame2.tbSame2_android_client.SameEnum2InterfaceClient\n"); + tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter tbSame2_sameEnum2Interface_service = new tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter(); + sb.append("Made instance of tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter\n"); + tbSame2.tbSame2_impl.SameEnum2InterfaceService tbSame2_sameEnum2Interface_local_impl = new tbSame2.tbSame2_impl.SameEnum2InterfaceService(); + sb.append("Made instance of tbSame2.tbSame2_impl.SameEnum2InterfaceService\n"); + + tbSimple.tbSimple_android_client.VoidInterfaceClient tbSimple_voidInterface_client = new tbSimple.tbSimple_android_client.VoidInterfaceClient(this.getApplicationContext(), "conn_tbSimple_voidInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.VoidInterfaceClient\n"); + tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter tbSimple_voidInterface_service = new tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.VoidInterfaceService tbSimple_voidInterface_local_impl = new tbSimple.tbSimple_impl.VoidInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.VoidInterfaceService\n"); + tbSimple.tbSimple_android_client.SimpleInterfaceClient tbSimple_simpleInterface_client = new tbSimple.tbSimple_android_client.SimpleInterfaceClient(this.getApplicationContext(), "conn_tbSimple_simpleInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.SimpleInterfaceClient\n"); + tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter tbSimple_simpleInterface_service = new tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.SimpleInterfaceService tbSimple_simpleInterface_local_impl = new tbSimple.tbSimple_impl.SimpleInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.SimpleInterfaceService\n"); + tbSimple.tbSimple_android_client.SimpleArrayInterfaceClient tbSimple_simpleArrayInterface_client = new tbSimple.tbSimple_android_client.SimpleArrayInterfaceClient(this.getApplicationContext(), "conn_tbSimple_simpleArrayInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.SimpleArrayInterfaceClient\n"); + tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter tbSimple_simpleArrayInterface_service = new tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.SimpleArrayInterfaceService tbSimple_simpleArrayInterface_local_impl = new tbSimple.tbSimple_impl.SimpleArrayInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.SimpleArrayInterfaceService\n"); + tbSimple.tbSimple_android_client.NoPropertiesInterfaceClient tbSimple_noPropertiesInterface_client = new tbSimple.tbSimple_android_client.NoPropertiesInterfaceClient(this.getApplicationContext(), "conn_tbSimple_noPropertiesInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.NoPropertiesInterfaceClient\n"); + tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter tbSimple_noPropertiesInterface_service = new tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.NoPropertiesInterfaceService tbSimple_noPropertiesInterface_local_impl = new tbSimple.tbSimple_impl.NoPropertiesInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.NoPropertiesInterfaceService\n"); + tbSimple.tbSimple_android_client.NoOperationsInterfaceClient tbSimple_noOperationsInterface_client = new tbSimple.tbSimple_android_client.NoOperationsInterfaceClient(this.getApplicationContext(), "conn_tbSimple_noOperationsInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.NoOperationsInterfaceClient\n"); + tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter tbSimple_noOperationsInterface_service = new tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.NoOperationsInterfaceService tbSimple_noOperationsInterface_local_impl = new tbSimple.tbSimple_impl.NoOperationsInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.NoOperationsInterfaceService\n"); + tbSimple.tbSimple_android_client.NoSignalsInterfaceClient tbSimple_noSignalsInterface_client = new tbSimple.tbSimple_android_client.NoSignalsInterfaceClient(this.getApplicationContext(), "conn_tbSimple_noSignalsInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.NoSignalsInterfaceClient\n"); + tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter tbSimple_noSignalsInterface_service = new tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.NoSignalsInterfaceService tbSimple_noSignalsInterface_local_impl = new tbSimple.tbSimple_impl.NoSignalsInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.NoSignalsInterfaceService\n"); + tbSimple.tbSimple_android_client.EmptyInterfaceClient tbSimple_emptyInterface_client = new tbSimple.tbSimple_android_client.EmptyInterfaceClient(this.getApplicationContext(), "conn_tbSimple_emptyInterface_client"); + sb.append("Made instance of tbSimple.tbSimple_android_client.EmptyInterfaceClient\n"); + tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter tbSimple_emptyInterface_service = new tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter(); + sb.append("Made instance of tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter\n"); + tbSimple.tbSimple_impl.EmptyInterfaceService tbSimple_emptyInterface_local_impl = new tbSimple.tbSimple_impl.EmptyInterfaceService(); + sb.append("Made instance of tbSimple.tbSimple_impl.EmptyInterfaceService\n"); + + testbed1.testbed1_android_client.StructInterfaceClient testbed1_structInterface_client = new testbed1.testbed1_android_client.StructInterfaceClient(this.getApplicationContext(), "conn_testbed1_structInterface_client"); + sb.append("Made instance of testbed1.testbed1_android_client.StructInterfaceClient\n"); + testbed1.testbed1_android_service.StructInterfaceServiceAdapter testbed1_structInterface_service = new testbed1.testbed1_android_service.StructInterfaceServiceAdapter(); + sb.append("Made instance of testbed1.testbed1_android_service.StructInterfaceServiceAdapter\n"); + testbed1.testbed1_impl.StructInterfaceService testbed1_structInterface_local_impl = new testbed1.testbed1_impl.StructInterfaceService(); + sb.append("Made instance of testbed1.testbed1_impl.StructInterfaceService\n"); + testbed1.testbed1_android_client.StructArrayInterfaceClient testbed1_structArrayInterface_client = new testbed1.testbed1_android_client.StructArrayInterfaceClient(this.getApplicationContext(), "conn_testbed1_structArrayInterface_client"); + sb.append("Made instance of testbed1.testbed1_android_client.StructArrayInterfaceClient\n"); + testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter testbed1_structArrayInterface_service = new testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter(); + sb.append("Made instance of testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter\n"); + testbed1.testbed1_impl.StructArrayInterfaceService testbed1_structArrayInterface_local_impl = new testbed1.testbed1_impl.StructArrayInterfaceService(); + sb.append("Made instance of testbed1.testbed1_impl.StructArrayInterfaceService\n"); + + // Show output on screen + TextView tv = new TextView(this); + tv.setText(sb.toString() + "\nPress Back to exit"); + setContentView(tv); + + } +} \ No newline at end of file diff --git a/goldenmaster/goldenmaster_example/src/main/res/values/strings.xml b/goldenmaster/goldenmaster_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..024d5aa --- /dev/null +++ b/goldenmaster/goldenmaster_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + GoldenmasterMainActivity + \ No newline at end of file diff --git a/goldenmaster/gradle.properties b/goldenmaster/gradle.properties new file mode 100644 index 0000000..e3bfeab --- /dev/null +++ b/goldenmaster/gradle.properties @@ -0,0 +1,21 @@ + # Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/goldenmaster/gradle/libs.versions.toml b/goldenmaster/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/settings.gradle b/goldenmaster/settings.gradle new file mode 100644 index 0000000..07d4392 --- /dev/null +++ b/goldenmaster/settings.gradle @@ -0,0 +1,39 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + includeGroupByRegex("org\\.jetbrains.*") // allow Kotlin + } + } + mavenCentral() + gradlePluginPortal() + } +} + + +rootProject.name = "goldenmaster" +include ':goldenmaster_example' +includeBuild 'testbed2' +includeBuild 'tbEnum' +includeBuild 'tbNames' +includeBuild 'tbSame1' +includeBuild 'tbSame2' +includeBuild 'tbSimple' +includeBuild 'testbed1' + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + // your libs.versions.toml will be picked up automatically + } + } + +} diff --git a/goldenmaster/tb.enum.api/TbEnum.java b/goldenmaster/tb.enum.api/TbEnum.java deleted file mode 100644 index f59dc1b..0000000 --- a/goldenmaster/tb.enum.api/TbEnum.java +++ /dev/null @@ -1,152 +0,0 @@ -package tb.enum.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class TbEnum { - - // enumerations - public enum Enum0 { - @JsonProperty("0") - Value0, - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - } - public enum Enum1 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - @JsonProperty("3") - Value3, - } - public enum Enum2 { - @JsonProperty("2") - Value2, - @JsonProperty("1") - Value1, - @JsonProperty("0") - Value0, - } - public enum Enum3 { - @JsonProperty("3") - Value3, - @JsonProperty("2") - Value2, - @JsonProperty("1") - Value1, - } - - // data structures - - // interfaces - public static interface IEnumInterfaceEventListener { - void onProp0Changed(Enum0 oldValue, Enum0 newValue); - void onProp1Changed(Enum1 oldValue, Enum1 newValue); - void onProp2Changed(Enum2 oldValue, Enum2 newValue); - void onProp3Changed(Enum3 oldValue, Enum3 newValue); - void onSig0(Enum0 param0); - void onSig1(Enum1 param1); - void onSig2(Enum2 param2); - void onSig3(Enum3 param3); - } - - public static interface IEnumInterface { - // properties - void setProp0(Enum0 prop0); - Enum0 getProp0(); - void fireProp0Changed(Enum0 oldValue, Enum0 newValue); - - void setProp1(Enum1 prop1); - Enum1 getProp1(); - void fireProp1Changed(Enum1 oldValue, Enum1 newValue); - - void setProp2(Enum2 prop2); - Enum2 getProp2(); - void fireProp2Changed(Enum2 oldValue, Enum2 newValue); - - void setProp3(Enum3 prop3); - Enum3 getProp3(); - void fireProp3Changed(Enum3 oldValue, Enum3 newValue); - - // methods - Enum0 func0(Enum0 param0); - Enum1 func1(Enum1 param1); - Enum2 func2(Enum2 param2); - Enum3 func3(Enum3 param3); - - // signal listeners - void addEventListener(IEnumInterfaceEventListener listener); - void removeEventListener(IEnumInterfaceEventListener listener); - } - - public static class AbstractEnumInterface implements IEnumInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp0Changed(Enum0 oldValue, Enum0 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp0Changed(oldValue, newValue); - } - } - - @Override - public void fireProp1Changed(Enum1 oldValue, Enum1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(Enum2 oldValue, Enum2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireProp3Changed(Enum3 oldValue, Enum3 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp3Changed(oldValue, newValue); - } - } - - @Override - public void fireSig0(Enum0 param0) { - for (ISig0EventListener listener : events) { - listener.onSig0(param0); - } - } - - @Override - public void fireSig1(Enum1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(Enum2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param2); - } - } - - @Override - public void fireSig3(Enum3 param3) { - for (ISig3EventListener listener : events) { - listener.onSig3(param3); - } - } - - - } -} \ No newline at end of file diff --git a/goldenmaster/tb.names.api/TbNames.java b/goldenmaster/tb.names.api/TbNames.java deleted file mode 100644 index 4124fd2..0000000 --- a/goldenmaster/tb.names.api/TbNames.java +++ /dev/null @@ -1,90 +0,0 @@ -package tb.names.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class TbNames { - - // enumerations - - // data structures - - // interfaces - public static interface INamEsEventListener { - void onSwitchChanged(boolean oldValue, boolean newValue); - void onSomePropertyChanged(int oldValue, int newValue); - void onSomePoperty2Changed(int oldValue, int newValue); - void onSomeSignal(boolean SOME_PARAM); - void onSomeSignal2(boolean Some_Param); - } - - public static interface INamEs { - // properties - void setSwitch(boolean Switch); - boolean getSwitch(); - void fireSwitchChanged(boolean oldValue, boolean newValue); - - void setSomeProperty(int SOME_PROPERTY); - int getSomeProperty(); - void fireSomePropertyChanged(int oldValue, int newValue); - - void setSomePoperty2(int Some_Poperty2); - int getSomePoperty2(); - void fireSomePoperty2Changed(int oldValue, int newValue); - - // methods - void someFunction(boolean SOME_PARAM); - void someFunction2(boolean Some_Param); - - // signal listeners - void addEventListener(INamEsEventListener listener); - void removeEventListener(INamEsEventListener listener); - } - - public static class AbstractNamEs implements INamEs { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireSwitchChanged(boolean oldValue, boolean newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onSwitchChanged(oldValue, newValue); - } - } - - @Override - public void fireSomePropertyChanged(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onSomePropertyChanged(oldValue, newValue); - } - } - - @Override - public void fireSomePoperty2Changed(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onSomePoperty2Changed(oldValue, newValue); - } - } - - @Override - public void fireSomeSignal(boolean SOME_PARAM) { - for (ISomeSignalEventListener listener : events) { - listener.onSomeSignal(SOME_PARAM); - } - } - - @Override - public void fireSomeSignal2(boolean Some_Param) { - for (ISomeSignal2EventListener listener : events) { - listener.onSomeSignal2(Some_Param); - } - } - - - } -} \ No newline at end of file diff --git a/goldenmaster/tb.same1.api/TbSame1.java b/goldenmaster/tb.same1.api/TbSame1.java deleted file mode 100644 index a6fe99f..0000000 --- a/goldenmaster/tb.same1.api/TbSame1.java +++ /dev/null @@ -1,269 +0,0 @@ -package tb.same1.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class TbSame1 { - - // enumerations - public enum Enum1 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - } - public enum Enum2 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - } - - // data structures - public static class Struct1 { - public Struct1(int field1, int field2, int field3) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - @JsonProperty("field3") - public int field3; - } - public static class Struct2 { - public Struct2(int field1, int field2, int field3) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - @JsonProperty("field3") - public int field3; - } - - // interfaces - public static interface ISameStruct1InterfaceEventListener { - void onProp1Changed(Struct1 oldValue, Struct1 newValue); - void onSig1(Struct1 param1); - } - - public static interface ISameStruct1Interface { - // properties - void setProp1(Struct1 prop1); - Struct1 getProp1(); - void fireProp1Changed(Struct1 oldValue, Struct1 newValue); - - // methods - Struct1 func1(Struct1 param1); - - // signal listeners - void addEventListener(ISameStruct1InterfaceEventListener listener); - void removeEventListener(ISameStruct1InterfaceEventListener listener); - } - - public static class AbstractSameStruct1Interface implements ISameStruct1Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Struct1 oldValue, Struct1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Struct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - - } - public static interface ISameStruct2InterfaceEventListener { - void onProp1Changed(Struct2 oldValue, Struct2 newValue); - void onProp2Changed(Struct2 oldValue, Struct2 newValue); - void onSig1(Struct1 param1); - void onSig2(Struct1 param1, Struct2 param2); - } - - public static interface ISameStruct2Interface { - // properties - void setProp1(Struct2 prop1); - Struct2 getProp1(); - void fireProp1Changed(Struct2 oldValue, Struct2 newValue); - - void setProp2(Struct2 prop2); - Struct2 getProp2(); - void fireProp2Changed(Struct2 oldValue, Struct2 newValue); - - // methods - Struct1 func1(Struct1 param1); - Struct1 func2(Struct1 param1, Struct2 param2); - - // signal listeners - void addEventListener(ISameStruct2InterfaceEventListener listener); - void removeEventListener(ISameStruct2InterfaceEventListener listener); - } - - public static class AbstractSameStruct2Interface implements ISameStruct2Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Struct2 oldValue, Struct2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(Struct2 oldValue, Struct2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Struct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(Struct1 param1, Struct2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - - } - public static interface ISameEnum1InterfaceEventListener { - void onProp1Changed(Enum1 oldValue, Enum1 newValue); - void onSig1(Enum1 param1); - } - - public static interface ISameEnum1Interface { - // properties - void setProp1(Enum1 prop1); - Enum1 getProp1(); - void fireProp1Changed(Enum1 oldValue, Enum1 newValue); - - // methods - Enum1 func1(Enum1 param1); - - // signal listeners - void addEventListener(ISameEnum1InterfaceEventListener listener); - void removeEventListener(ISameEnum1InterfaceEventListener listener); - } - - public static class AbstractSameEnum1Interface implements ISameEnum1Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Enum1 oldValue, Enum1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Enum1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - - } - public static interface ISameEnum2InterfaceEventListener { - void onProp1Changed(Enum1 oldValue, Enum1 newValue); - void onProp2Changed(Enum2 oldValue, Enum2 newValue); - void onSig1(Enum1 param1); - void onSig2(Enum1 param1, Enum2 param2); - } - - public static interface ISameEnum2Interface { - // properties - void setProp1(Enum1 prop1); - Enum1 getProp1(); - void fireProp1Changed(Enum1 oldValue, Enum1 newValue); - - void setProp2(Enum2 prop2); - Enum2 getProp2(); - void fireProp2Changed(Enum2 oldValue, Enum2 newValue); - - // methods - Enum1 func1(Enum1 param1); - Enum1 func2(Enum1 param1, Enum2 param2); - - // signal listeners - void addEventListener(ISameEnum2InterfaceEventListener listener); - void removeEventListener(ISameEnum2InterfaceEventListener listener); - } - - public static class AbstractSameEnum2Interface implements ISameEnum2Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Enum1 oldValue, Enum1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(Enum2 oldValue, Enum2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Enum1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(Enum1 param1, Enum2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - - } -} \ No newline at end of file diff --git a/goldenmaster/tb.same2.api/TbSame2.java b/goldenmaster/tb.same2.api/TbSame2.java deleted file mode 100644 index 36cb671..0000000 --- a/goldenmaster/tb.same2.api/TbSame2.java +++ /dev/null @@ -1,269 +0,0 @@ -package tb.same2.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class TbSame2 { - - // enumerations - public enum Enum1 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - } - public enum Enum2 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - } - - // data structures - public static class Struct1 { - public Struct1(int field1, int field2, int field3) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - @JsonProperty("field3") - public int field3; - } - public static class Struct2 { - public Struct2(int field1, int field2, int field3) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - @JsonProperty("field3") - public int field3; - } - - // interfaces - public static interface ISameStruct1InterfaceEventListener { - void onProp1Changed(Struct1 oldValue, Struct1 newValue); - void onSig1(Struct1 param1); - } - - public static interface ISameStruct1Interface { - // properties - void setProp1(Struct1 prop1); - Struct1 getProp1(); - void fireProp1Changed(Struct1 oldValue, Struct1 newValue); - - // methods - Struct1 func1(Struct1 param1); - - // signal listeners - void addEventListener(ISameStruct1InterfaceEventListener listener); - void removeEventListener(ISameStruct1InterfaceEventListener listener); - } - - public static class AbstractSameStruct1Interface implements ISameStruct1Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Struct1 oldValue, Struct1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Struct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - - } - public static interface ISameStruct2InterfaceEventListener { - void onProp1Changed(Struct2 oldValue, Struct2 newValue); - void onProp2Changed(Struct2 oldValue, Struct2 newValue); - void onSig1(Struct1 param1); - void onSig2(Struct1 param1, Struct2 param2); - } - - public static interface ISameStruct2Interface { - // properties - void setProp1(Struct2 prop1); - Struct2 getProp1(); - void fireProp1Changed(Struct2 oldValue, Struct2 newValue); - - void setProp2(Struct2 prop2); - Struct2 getProp2(); - void fireProp2Changed(Struct2 oldValue, Struct2 newValue); - - // methods - Struct1 func1(Struct1 param1); - Struct1 func2(Struct1 param1, Struct2 param2); - - // signal listeners - void addEventListener(ISameStruct2InterfaceEventListener listener); - void removeEventListener(ISameStruct2InterfaceEventListener listener); - } - - public static class AbstractSameStruct2Interface implements ISameStruct2Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Struct2 oldValue, Struct2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(Struct2 oldValue, Struct2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Struct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(Struct1 param1, Struct2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - - } - public static interface ISameEnum1InterfaceEventListener { - void onProp1Changed(Enum1 oldValue, Enum1 newValue); - void onSig1(Enum1 param1); - } - - public static interface ISameEnum1Interface { - // properties - void setProp1(Enum1 prop1); - Enum1 getProp1(); - void fireProp1Changed(Enum1 oldValue, Enum1 newValue); - - // methods - Enum1 func1(Enum1 param1); - - // signal listeners - void addEventListener(ISameEnum1InterfaceEventListener listener); - void removeEventListener(ISameEnum1InterfaceEventListener listener); - } - - public static class AbstractSameEnum1Interface implements ISameEnum1Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Enum1 oldValue, Enum1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Enum1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - - } - public static interface ISameEnum2InterfaceEventListener { - void onProp1Changed(Enum1 oldValue, Enum1 newValue); - void onProp2Changed(Enum2 oldValue, Enum2 newValue); - void onSig1(Enum1 param1); - void onSig2(Enum1 param1, Enum2 param2); - } - - public static interface ISameEnum2Interface { - // properties - void setProp1(Enum1 prop1); - Enum1 getProp1(); - void fireProp1Changed(Enum1 oldValue, Enum1 newValue); - - void setProp2(Enum2 prop2); - Enum2 getProp2(); - void fireProp2Changed(Enum2 oldValue, Enum2 newValue); - - // methods - Enum1 func1(Enum1 param1); - Enum1 func2(Enum1 param1, Enum2 param2); - - // signal listeners - void addEventListener(ISameEnum2InterfaceEventListener listener); - void removeEventListener(ISameEnum2InterfaceEventListener listener); - } - - public static class AbstractSameEnum2Interface implements ISameEnum2Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(Enum1 oldValue, Enum1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(Enum2 oldValue, Enum2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(Enum1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(Enum1 param1, Enum2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - - } -} \ No newline at end of file diff --git a/goldenmaster/tb.simple.api/TbSimple.java b/goldenmaster/tb.simple.api/TbSimple.java deleted file mode 100644 index a425e97..0000000 --- a/goldenmaster/tb.simple.api/TbSimple.java +++ /dev/null @@ -1,616 +0,0 @@ -package tb.simple.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class TbSimple { - - // enumerations - - // data structures - - // interfaces - public static interface IVoidInterfaceEventListener { - void onSigVoid(); - } - - public static interface IVoidInterface { - // properties - // methods - void funcVoid(); - - // signal listeners - void addEventListener(IVoidInterfaceEventListener listener); - void removeEventListener(IVoidInterfaceEventListener listener); - } - - public static class AbstractVoidInterface implements IVoidInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireSigVoid() { - for (ISigVoidEventListener listener : events) { - listener.onSigVoid(); - } - } - - - } - public static interface ISimpleInterfaceEventListener { - void onPropBoolChanged(boolean oldValue, boolean newValue); - void onPropIntChanged(int oldValue, int newValue); - void onPropInt32Changed(int oldValue, int newValue); - void onPropInt64Changed(long oldValue, long newValue); - void onPropFloatChanged(float oldValue, float newValue); - void onPropFloat32Changed(float oldValue, float newValue); - void onPropFloat64Changed(double oldValue, double newValue); - void onPropStringChanged(String oldValue, String newValue); - void onSigBool(boolean paramBool); - void onSigInt(int paramInt); - void onSigInt32(int paramInt32); - void onSigInt64(long paramInt64); - void onSigFloat(float paramFloat); - void onSigFloat32(float paramFloat32); - void onSigFloat64(double paramFloat64); - void onSigString(String paramString); - } - - public static interface ISimpleInterface { - // properties - void setPropBool(boolean propBool); - boolean getPropBool(); - void firePropBoolChanged(boolean oldValue, boolean newValue); - - void setPropInt(int propInt); - int getPropInt(); - void firePropIntChanged(int oldValue, int newValue); - - void setPropInt32(int propInt32); - int getPropInt32(); - void firePropInt32Changed(int oldValue, int newValue); - - void setPropInt64(long propInt64); - long getPropInt64(); - void firePropInt64Changed(long oldValue, long newValue); - - void setPropFloat(float propFloat); - float getPropFloat(); - void firePropFloatChanged(float oldValue, float newValue); - - void setPropFloat32(float propFloat32); - float getPropFloat32(); - void firePropFloat32Changed(float oldValue, float newValue); - - void setPropFloat64(double propFloat64); - double getPropFloat64(); - void firePropFloat64Changed(double oldValue, double newValue); - - void setPropString(String propString); - String getPropString(); - void firePropStringChanged(String oldValue, String newValue); - - // methods - void funcNoReturnValue(boolean paramBool); - boolean funcBool(boolean paramBool); - int funcInt(int paramInt); - int funcInt32(int paramInt32); - long funcInt64(long paramInt64); - float funcFloat(float paramFloat); - float funcFloat32(float paramFloat32); - double funcFloat64(double paramFloat); - String funcString(String paramString); - - // signal listeners - void addEventListener(ISimpleInterfaceEventListener listener); - void removeEventListener(ISimpleInterfaceEventListener listener); - } - - public static class AbstractSimpleInterface implements ISimpleInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void firePropBoolChanged(boolean oldValue, boolean newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropBoolChanged(oldValue, newValue); - } - } - - @Override - public void firePropIntChanged(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropIntChanged(oldValue, newValue); - } - } - - @Override - public void firePropInt32Changed(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropInt32Changed(oldValue, newValue); - } - } - - @Override - public void firePropInt64Changed(long oldValue, long newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropInt64Changed(oldValue, newValue); - } - } - - @Override - public void firePropFloatChanged(float oldValue, float newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloatChanged(oldValue, newValue); - } - } - - @Override - public void firePropFloat32Changed(float oldValue, float newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloat32Changed(oldValue, newValue); - } - } - - @Override - public void firePropFloat64Changed(double oldValue, double newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloat64Changed(oldValue, newValue); - } - } - - @Override - public void firePropStringChanged(String oldValue, String newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropStringChanged(oldValue, newValue); - } - } - - @Override - public void fireSigBool(boolean paramBool) { - for (ISigBoolEventListener listener : events) { - listener.onSigBool(paramBool); - } - } - - @Override - public void fireSigInt(int paramInt) { - for (ISigIntEventListener listener : events) { - listener.onSigInt(paramInt); - } - } - - @Override - public void fireSigInt32(int paramInt32) { - for (ISigInt32EventListener listener : events) { - listener.onSigInt32(paramInt32); - } - } - - @Override - public void fireSigInt64(long paramInt64) { - for (ISigInt64EventListener listener : events) { - listener.onSigInt64(paramInt64); - } - } - - @Override - public void fireSigFloat(float paramFloat) { - for (ISigFloatEventListener listener : events) { - listener.onSigFloat(paramFloat); - } - } - - @Override - public void fireSigFloat32(float paramFloat32) { - for (ISigFloat32EventListener listener : events) { - listener.onSigFloat32(paramFloat32); - } - } - - @Override - public void fireSigFloat64(double paramFloat64) { - for (ISigFloat64EventListener listener : events) { - listener.onSigFloat64(paramFloat64); - } - } - - @Override - public void fireSigString(String paramString) { - for (ISigStringEventListener listener : events) { - listener.onSigString(paramString); - } - } - - - } - public static interface ISimpleArrayInterfaceEventListener { - void onPropBoolChanged(boolean[] oldValue, boolean[] newValue); - void onPropIntChanged(int[] oldValue, int[] newValue); - void onPropInt32Changed(int[] oldValue, int[] newValue); - void onPropInt64Changed(long[] oldValue, long[] newValue); - void onPropFloatChanged(float[] oldValue, float[] newValue); - void onPropFloat32Changed(float[] oldValue, float[] newValue); - void onPropFloat64Changed(double[] oldValue, double[] newValue); - void onPropStringChanged(String[] oldValue, String[] newValue); - void onPropReadOnlyStringChanged(String oldValue, String newValue); - void onSigBool(boolean[] paramBool); - void onSigInt(int[] paramInt); - void onSigInt32(int[] paramInt32); - void onSigInt64(long[] paramInt64); - void onSigFloat(float[] paramFloat); - void onSigFloat32(float[] paramFloa32); - void onSigFloat64(double[] paramFloat64); - void onSigString(String[] paramString); - } - - public static interface ISimpleArrayInterface { - // properties - void setPropBool(boolean[] propBool); - boolean[] getPropBool(); - void firePropBoolChanged(boolean[] oldValue, boolean[] newValue); - - void setPropInt(int[] propInt); - int[] getPropInt(); - void firePropIntChanged(int[] oldValue, int[] newValue); - - void setPropInt32(int[] propInt32); - int[] getPropInt32(); - void firePropInt32Changed(int[] oldValue, int[] newValue); - - void setPropInt64(long[] propInt64); - long[] getPropInt64(); - void firePropInt64Changed(long[] oldValue, long[] newValue); - - void setPropFloat(float[] propFloat); - float[] getPropFloat(); - void firePropFloatChanged(float[] oldValue, float[] newValue); - - void setPropFloat32(float[] propFloat32); - float[] getPropFloat32(); - void firePropFloat32Changed(float[] oldValue, float[] newValue); - - void setPropFloat64(double[] propFloat64); - double[] getPropFloat64(); - void firePropFloat64Changed(double[] oldValue, double[] newValue); - - void setPropString(String[] propString); - String[] getPropString(); - void firePropStringChanged(String[] oldValue, String[] newValue); - - void setPropReadOnlyString(String propReadOnlyString); - String getPropReadOnlyString(); - void firePropReadOnlyStringChanged(String oldValue, String newValue); - - // methods - boolean[] funcBool(boolean[] paramBool); - int[] funcInt(int[] paramInt); - int[] funcInt32(int[] paramInt32); - long[] funcInt64(long[] paramInt64); - float[] funcFloat(float[] paramFloat); - float[] funcFloat32(float[] paramFloat32); - double[] funcFloat64(double[] paramFloat); - String[] funcString(String[] paramString); - - // signal listeners - void addEventListener(ISimpleArrayInterfaceEventListener listener); - void removeEventListener(ISimpleArrayInterfaceEventListener listener); - } - - public static class AbstractSimpleArrayInterface implements ISimpleArrayInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void firePropBoolChanged(boolean[] oldValue, boolean[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropBoolChanged(oldValue, newValue); - } - } - - @Override - public void firePropIntChanged(int[] oldValue, int[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropIntChanged(oldValue, newValue); - } - } - - @Override - public void firePropInt32Changed(int[] oldValue, int[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropInt32Changed(oldValue, newValue); - } - } - - @Override - public void firePropInt64Changed(long[] oldValue, long[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropInt64Changed(oldValue, newValue); - } - } - - @Override - public void firePropFloatChanged(float[] oldValue, float[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloatChanged(oldValue, newValue); - } - } - - @Override - public void firePropFloat32Changed(float[] oldValue, float[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloat32Changed(oldValue, newValue); - } - } - - @Override - public void firePropFloat64Changed(double[] oldValue, double[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloat64Changed(oldValue, newValue); - } - } - - @Override - public void firePropStringChanged(String[] oldValue, String[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropStringChanged(oldValue, newValue); - } - } - - @Override - public void firePropReadOnlyStringChanged(String oldValue, String newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropReadOnlyStringChanged(oldValue, newValue); - } - } - - @Override - public void fireSigBool(boolean[] paramBool) { - for (ISigBoolEventListener listener : events) { - listener.onSigBool(paramBool); - } - } - - @Override - public void fireSigInt(int[] paramInt) { - for (ISigIntEventListener listener : events) { - listener.onSigInt(paramInt); - } - } - - @Override - public void fireSigInt32(int[] paramInt32) { - for (ISigInt32EventListener listener : events) { - listener.onSigInt32(paramInt32); - } - } - - @Override - public void fireSigInt64(long[] paramInt64) { - for (ISigInt64EventListener listener : events) { - listener.onSigInt64(paramInt64); - } - } - - @Override - public void fireSigFloat(float[] paramFloat) { - for (ISigFloatEventListener listener : events) { - listener.onSigFloat(paramFloat); - } - } - - @Override - public void fireSigFloat32(float[] paramFloa32) { - for (ISigFloat32EventListener listener : events) { - listener.onSigFloat32(paramFloa32); - } - } - - @Override - public void fireSigFloat64(double[] paramFloat64) { - for (ISigFloat64EventListener listener : events) { - listener.onSigFloat64(paramFloat64); - } - } - - @Override - public void fireSigString(String[] paramString) { - for (ISigStringEventListener listener : events) { - listener.onSigString(paramString); - } - } - - - } - public static interface INoPropertiesInterfaceEventListener { - void onSigVoid(); - void onSigBool(boolean paramBool); - } - - public static interface INoPropertiesInterface { - // properties - // methods - void funcVoid(); - boolean funcBool(boolean paramBool); - - // signal listeners - void addEventListener(INoPropertiesInterfaceEventListener listener); - void removeEventListener(INoPropertiesInterfaceEventListener listener); - } - - public static class AbstractNoPropertiesInterface implements INoPropertiesInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireSigVoid() { - for (ISigVoidEventListener listener : events) { - listener.onSigVoid(); - } - } - - @Override - public void fireSigBool(boolean paramBool) { - for (ISigBoolEventListener listener : events) { - listener.onSigBool(paramBool); - } - } - - - } - public static interface INoOperationsInterfaceEventListener { - void onPropBoolChanged(boolean oldValue, boolean newValue); - void onPropIntChanged(int oldValue, int newValue); - void onSigVoid(); - void onSigBool(boolean paramBool); - } - - public static interface INoOperationsInterface { - // properties - void setPropBool(boolean propBool); - boolean getPropBool(); - void firePropBoolChanged(boolean oldValue, boolean newValue); - - void setPropInt(int propInt); - int getPropInt(); - void firePropIntChanged(int oldValue, int newValue); - - // methods - - // signal listeners - void addEventListener(INoOperationsInterfaceEventListener listener); - void removeEventListener(INoOperationsInterfaceEventListener listener); - } - - public static class AbstractNoOperationsInterface implements INoOperationsInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void firePropBoolChanged(boolean oldValue, boolean newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropBoolChanged(oldValue, newValue); - } - } - - @Override - public void firePropIntChanged(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropIntChanged(oldValue, newValue); - } - } - - @Override - public void fireSigVoid() { - for (ISigVoidEventListener listener : events) { - listener.onSigVoid(); - } - } - - @Override - public void fireSigBool(boolean paramBool) { - for (ISigBoolEventListener listener : events) { - listener.onSigBool(paramBool); - } - } - - - } - public static interface INoSignalsInterfaceEventListener { - void onPropBoolChanged(boolean oldValue, boolean newValue); - void onPropIntChanged(int oldValue, int newValue); - } - - public static interface INoSignalsInterface { - // properties - void setPropBool(boolean propBool); - boolean getPropBool(); - void firePropBoolChanged(boolean oldValue, boolean newValue); - - void setPropInt(int propInt); - int getPropInt(); - void firePropIntChanged(int oldValue, int newValue); - - // methods - void funcVoid(); - boolean funcBool(boolean paramBool); - - // signal listeners - void addEventListener(INoSignalsInterfaceEventListener listener); - void removeEventListener(INoSignalsInterfaceEventListener listener); - } - - public static class AbstractNoSignalsInterface implements INoSignalsInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void firePropBoolChanged(boolean oldValue, boolean newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropBoolChanged(oldValue, newValue); - } - } - - @Override - public void firePropIntChanged(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropIntChanged(oldValue, newValue); - } - } - - - } - public static interface IEmptyInterfaceEventListener { - } - - public static interface IEmptyInterface { - // properties - // methods - - // signal listeners - void addEventListener(IEmptyInterfaceEventListener listener); - void removeEventListener(IEmptyInterfaceEventListener listener); - } - - public static class AbstractEmptyInterface implements IEmptyInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - - } -} \ No newline at end of file diff --git a/goldenmaster/tbEnum/gradle.properties b/goldenmaster/tbEnum/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbEnum/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbEnum/gradle/libs.versions.toml b/goldenmaster/tbEnum/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbEnum/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbEnum/settings.gradle b/goldenmaster/tbEnum/settings.gradle new file mode 100644 index 0000000..83a46c2 --- /dev/null +++ b/goldenmaster/tbEnum/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbEnum" +include ':tbEnum_android_service' +include ':tbEnum_android_client' +include ':tbEnum_android_messenger' +include ':tbEnum_impl' +include ':tbEnum_api' +include ':tbEnum_client_example' +include ':tbEnumserviceexample' \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle b/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle new file mode 100644 index 0000000..8f3f3f0 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'tbEnum.tbEnum_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + implementation project(':tbEnum_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_android_client/build.gradle b/goldenmaster/tbEnum/tbEnum_android_client/build.gradle new file mode 100644 index 0000000..6af1f72 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbEnum" +version = "1.0.0" + +android { + namespace 'tbEnum.tbEnum_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + implementation project(':tbEnum_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java new file mode 100644 index 0000000..a5685a5 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java @@ -0,0 +1,717 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbEnum.tbEnum_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; +import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class EnumInterfaceClient extends AbstractEnumInterface implements ServiceConnection +{ + private static final String TAG = "EnumInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Enum0 m_prop0 = Enum0.Value0; + private Enum1 m_prop1 = Enum1.Value1; + private Enum2 m_prop2 = Enum2.Value2; + private Enum3 m_prop3 = Enum3.Value3; + + + public EnumInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type EnumInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, EnumInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, EnumInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (EnumInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + + Enum0 prop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); + onProp0(prop0); + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + onProp1(prop1); + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + onProp2(prop2); + + Enum3 prop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); + onProp3(prop3); + + break; + } + case SET_Prop0: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + + Enum0 prop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); + + onProp0(prop0); + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + onProp2(prop2); + break; + } + case SET_Prop3: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + + Enum3 prop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); + + onProp3(prop3); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig0: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0 param0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); + onSig0(param0); + break; + } + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + onSig2(param2); + break; + } + case SIG_Sig3: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + Enum3 param3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); + onSig3(param3); + break; + } + case RPC_Func0Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received EnumInterfaceMessageType.RPC_Func0Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received EnumInterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received EnumInterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func3Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received EnumInterfaceMessageType.RPC_Func3Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp0(Enum0 prop0) + { + Log.i(TAG, "request setProp0 called "+ prop0); + if (m_prop0 != prop0) + { + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.PROP_Prop0.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop0", new Enum0Parcelable(prop0)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp0(Enum0 prop0) + { + Log.i(TAG, "value received from service for Prop0 "); + if (m_prop0 != prop0) + { + m_prop0 = prop0; + fireProp0Changed(prop0); + } + + } + + @Override + public Enum0 getProp0() + { + Log.i(TAG, "request getProp0 called, returning local"); + return m_prop0; + } + + + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (m_prop1 != prop1) + { + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Enum1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (m_prop2 != prop2) + { + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(Enum2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + @Override + public void setProp3(Enum3 prop3) + { + Log.i(TAG, "request setProp3 called "+ prop3); + if (m_prop3 != prop3) + { + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.PROP_Prop3.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop3", new Enum3Parcelable(prop3)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp3(Enum3 prop3) + { + Log.i(TAG, "value received from service for Prop3 "); + if (m_prop3 != prop3) + { + m_prop3 = prop3; + fireProp3Changed(prop3); + } + + } + + @Override + public Enum3 getProp3() + { + Log.i(TAG, "request getProp3 called, returning local"); + return m_prop3; + } + + + // methods + + + @Override + public Enum0 func0(Enum0 param0) { + CompletableFuture resFuture = func0Async(param0); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func0Async(Enum0 param0) { + + Log.i(TAG, "Call on service func0 "+ " " + param0); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.RPC_Func0Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param0", new Enum0Parcelable(param0)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum0 result = bundle.getParcelable("result", Enum0Parcelable.class).getEnum0(); + Log.v(TAG, "resolve func0" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Enum1 func1(Enum1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Enum2 func2(Enum2 param2) { + CompletableFuture resFuture = func2Async(param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(Enum2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param2); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param2", new Enum2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum2 result = bundle.getParcelable("result", Enum2Parcelable.class).getEnum2(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Enum3 func3(Enum3 param3) { + CompletableFuture resFuture = func3Async(param3); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func3Async(Enum3 param3) { + + Log.i(TAG, "Call on service func3 "+ " " + param3); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.RPC_Func3Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param3", new Enum3Parcelable(param3)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum3 result = bundle.getParcelable("result", Enum3Parcelable.class).getEnum3(); + Log.v(TAG, "resolve func3" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig0(Enum0 param0) + { + Log.i(TAG, "onSig0 received from service"); + fireSig0(param0); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(Enum2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param2); + } + public void onSig3(Enum3 param3) + { + Log.i(TAG, "onSig3 received from service"); + fireSig3(param3); + } +} diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java new file mode 100644 index 0000000..44a95b9 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java @@ -0,0 +1,542 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbEnum.tbEnum_android_client; + +import tbEnum.tbEnum_android_client.EnumInterfaceClient; + +//import message type and parcelabe types +import tbEnum.tbEnum_api.TbEnumTestHelper; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; +import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IEnumInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class EnumInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private EnumInterfaceClient testedClient; + private IEnumInterfaceEventListener listenerMock = mock(IEnumInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IEnumInterfaceClientMessageGetter serviceMessagesStorage = mock(IEnumInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(EnumInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IEnumInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new EnumInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbEnum.tbEnum_android_service", "tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(EnumInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, EnumInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Enum0 testprop0 = Enum0.Value1; + data.putParcelable("prop0", new Enum0Parcelable(testprop0)); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + Enum2 testprop2 = Enum2.Value1; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + Enum3 testprop3 = Enum3.Value2; + data.putParcelable("prop3", new Enum3Parcelable(testprop3)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp0Changed(testprop0); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop0PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.SET_Prop0.getValue()); + Bundle data = new Bundle(); + Enum0 testprop0 = Enum0.Value1; + data.putParcelable("prop0", new Enum0Parcelable(testprop0)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp0Changed(testprop0); + } + + @Test + public void setPropertyRequestprop0() + { + Enum0 testprop0 = Enum0.Value1; + + testedClient.setProp0(testprop0); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.PROP_Prop0.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0 receivedprop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); + assertEquals(receivedprop0, testprop0); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } + + @Test + public void setPropertyRequestprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + Enum2 testprop2 = Enum2.Value1; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + } + + @Test + public void setPropertyRequestprop2() + { + Enum2 testprop2 = Enum2.Value1; + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedprop2, testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop3PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.SET_Prop3.getValue()); + Bundle data = new Bundle(); + Enum3 testprop3 = Enum3.Value2; + data.putParcelable("prop3", new Enum3Parcelable(testprop3)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); + } + + @Test + public void setPropertyRequestprop3() + { + Enum3 testprop3 = Enum3.Value2; + + testedClient.setProp3(testprop3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.PROP_Prop3.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + Enum3 receivedprop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); + assertEquals(receivedprop3, testprop3); + } + @Test + public void whenNotifiedsig0() throws RemoteException + { + + Message msg = Message.obtain(null, EnumInterfaceMessageType.SIG_Sig0.getValue()); + Bundle data = new Bundle(); + Enum0 testparam0 = Enum0.Value1; + data.putParcelable("param0", new Enum0Parcelable(testparam0)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig0(testparam0); + +} + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, EnumInterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1(testparam1); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, EnumInterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + Enum2 testparam2 = Enum2.Value1; + data.putParcelable("param2", new Enum2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2(testparam2); + +} + @Test + public void whenNotifiedsig3() throws RemoteException + { + + Message msg = Message.obtain(null, EnumInterfaceMessageType.SIG_Sig3.getValue()); + Bundle data = new Bundle(); + Enum3 testparam3 = Enum3.Value2; + data.putParcelable("param3", new Enum3Parcelable(testparam3)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig3(testparam3); + +} + + + public void onfunc0Request() throws RemoteException { + + // Execute method + Enum0 testparam0 = Enum0.Value1; + Enum0 expectedResult = Enum0.Value1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func0Async(testparam0); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(EnumInterfaceMessageType.RPC_Func0Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0 receivedparam0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); + assertEquals(receivedparam0, testparam0); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func0Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum0Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(EnumInterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + Enum2 testparam2 = Enum2.Value1; + Enum2 expectedResult = Enum2.Value1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(EnumInterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum2Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc3Request() throws RemoteException { + + // Execute method + Enum3 testparam3 = Enum3.Value2; + Enum3 expectedResult = Enum3.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func3Async(testparam3); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(EnumInterfaceMessageType.RPC_Func3Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + Enum3 receivedparam3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); + assertEquals(receivedparam3, testparam3); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func3Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum3Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/additions.gradle b/goldenmaster/tbEnum/tbEnum_android_messenger/additions.gradle new file mode 100644 index 0000000..99ec63b --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbEnum.tbEnum_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/build.gradle b/goldenmaster/tbEnum/tbEnum_android_messenger/build.gradle new file mode 100644 index 0000000..b6ce20c --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbEnum" +version = "1.0.0" + +android { + namespace 'tbEnum.tbEnum_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum0Parcelable.java b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum0Parcelable.java new file mode 100644 index 0000000..a87ff31 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum0Parcelable.java @@ -0,0 +1,69 @@ +package tbEnum.tbEnum_android_messenger; + +import tbEnum.tbEnum_api.Enum0; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum0Parcelable implements Parcelable { + + public Enum0 data; + + public Enum0Parcelable(Enum0 data) { + this.data = data; + } + + public Enum0 getEnum0() + { + return data; + } + + protected Enum0Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum0.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum0Parcelable createFromParcel(Parcel in) { + return new Enum0Parcelable(in); + } + + @Override + public Enum0Parcelable[] newArray(int size) { + return new Enum0Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum0Parcelable[] wrapArray(Enum0[] enums) { + if (enums == null) return null; + Enum0Parcelable[] result = new Enum0Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum0Parcelable(enums[i]); + } + return result; + } + + public static Enum0[] unwrapArray(Enum0Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum0[] out = new Enum0[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum0(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum1Parcelable.java b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum1Parcelable.java new file mode 100644 index 0000000..1f50e69 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum1Parcelable.java @@ -0,0 +1,69 @@ +package tbEnum.tbEnum_android_messenger; + +import tbEnum.tbEnum_api.Enum1; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum1Parcelable implements Parcelable { + + public Enum1 data; + + public Enum1Parcelable(Enum1 data) { + this.data = data; + } + + public Enum1 getEnum1() + { + return data; + } + + protected Enum1Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum1.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum1Parcelable createFromParcel(Parcel in) { + return new Enum1Parcelable(in); + } + + @Override + public Enum1Parcelable[] newArray(int size) { + return new Enum1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum1Parcelable[] wrapArray(Enum1[] enums) { + if (enums == null) return null; + Enum1Parcelable[] result = new Enum1Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum1Parcelable(enums[i]); + } + return result; + } + + public static Enum1[] unwrapArray(Enum1Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum1[] out = new Enum1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum2Parcelable.java b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum2Parcelable.java new file mode 100644 index 0000000..e94067a --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum2Parcelable.java @@ -0,0 +1,69 @@ +package tbEnum.tbEnum_android_messenger; + +import tbEnum.tbEnum_api.Enum2; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum2Parcelable implements Parcelable { + + public Enum2 data; + + public Enum2Parcelable(Enum2 data) { + this.data = data; + } + + public Enum2 getEnum2() + { + return data; + } + + protected Enum2Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum2.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum2Parcelable createFromParcel(Parcel in) { + return new Enum2Parcelable(in); + } + + @Override + public Enum2Parcelable[] newArray(int size) { + return new Enum2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum2Parcelable[] wrapArray(Enum2[] enums) { + if (enums == null) return null; + Enum2Parcelable[] result = new Enum2Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum2Parcelable(enums[i]); + } + return result; + } + + public static Enum2[] unwrapArray(Enum2Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum2[] out = new Enum2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum3Parcelable.java b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum3Parcelable.java new file mode 100644 index 0000000..ff41f4c --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/Enum3Parcelable.java @@ -0,0 +1,69 @@ +package tbEnum.tbEnum_android_messenger; + +import tbEnum.tbEnum_api.Enum3; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum3Parcelable implements Parcelable { + + public Enum3 data; + + public Enum3Parcelable(Enum3 data) { + this.data = data; + } + + public Enum3 getEnum3() + { + return data; + } + + protected Enum3Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum3.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum3Parcelable createFromParcel(Parcel in) { + return new Enum3Parcelable(in); + } + + @Override + public Enum3Parcelable[] newArray(int size) { + return new Enum3Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum3Parcelable[] wrapArray(Enum3[] enums) { + if (enums == null) return null; + Enum3Parcelable[] result = new Enum3Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum3Parcelable(enums[i]); + } + return result; + } + + public static Enum3[] unwrapArray(Enum3Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum3[] out = new Enum3[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum3(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceMessageType.java b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceMessageType.java new file mode 100644 index 0000000..6ea61ba --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceMessageType.java @@ -0,0 +1,50 @@ +package tbEnum.tbEnum_android_messenger; + +public enum EnumInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop0(3), + SET_Prop0(4), + PROP_Prop1(5), + SET_Prop1(6), + PROP_Prop2(7), + SET_Prop2(8), + PROP_Prop3(9), + SET_Prop3(10), + SIG_Sig0(11), + SIG_Sig1(12), + SIG_Sig2(13), + SIG_Sig3(14), + RPC_Func0Req(15), + RPC_Func0Resp(16), + RPC_Func1Req(17), + RPC_Func1Resp(18), + RPC_Func2Req(19), + RPC_Func2Resp(20), + RPC_Func3Req(21), + RPC_Func3Resp(22), + EnumInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + EnumInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static EnumInterfaceMessageType fromInteger(int value) + { + for (EnumInterfaceMessageType event : EnumInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return EnumInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle b/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle new file mode 100644 index 0000000..d204e1e --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbEnum.tbEnum_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + implementation project(':tbEnum_impl') + implementation project(':tbEnum_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_android_service/build.gradle b/goldenmaster/tbEnum/tbEnum_android_service/build.gradle new file mode 100644 index 0000000..2966915 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbEnum" +version = "1.0.0" + + +android { + namespace 'tbEnum.tbEnum_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + implementation project(':tbEnum_impl') + implementation project(':tbEnum_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbEnum/tbEnum_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f1fa68e --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java new file mode 100644 index 0000000..3409cec --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java @@ -0,0 +1,492 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbEnum.tbEnum_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; +import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_android_service.IEnumInterfaceServiceFactory; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class EnumInterfaceServiceAdapter extends Service +{ + private static final String TAG = "EnumInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IEnumInterface mBackendService; + private static IEnumInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public EnumInterfaceServiceAdapter() + { + } + + public static IEnumInterface setService(IEnumInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(EnumInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: EnumInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(EnumInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(EnumInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IEnumInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (EnumInterfaceMessageType.fromInteger(msg.what) != EnumInterfaceMessageType.REGISTER_CLIENT + && EnumInterfaceMessageType.fromInteger(msg.what) != EnumInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: EnumInterfaceMessageType" + EnumInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (EnumInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop0: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0 prop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); + mBackendService.setProp0(prop0); + break; + } + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + mBackendService.setProp2(prop2); + break; + } + case PROP_Prop3: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + Enum3 prop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); + mBackendService.setProp3(prop3); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func0Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum0 param0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); + + Enum0 result = mBackendService.func0(param0); + + Message respMsg = new Message(); + respMsg.what = EnumInterfaceMessageType.RPC_Func0Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum0Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = EnumInterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + + Enum2 result = mBackendService.func2(param2); + + Message respMsg = new Message(); + respMsg.what = EnumInterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum2Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func3Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum3 param3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); + + Enum3 result = mBackendService.func3(param3); + + Message respMsg = new Message(); + respMsg.what = EnumInterfaceMessageType.RPC_Func3Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum3Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Enum0 prop0 = mBackendService.getProp0(); + + data.putParcelable("prop0", new Enum0Parcelable(prop0)); + Enum1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + Enum2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + Enum3 prop3 = mBackendService.getProp3(); + + data.putParcelable("prop3", new Enum3Parcelable(prop3)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp0Changed(Enum0 prop0){ + Log.i(TAG, "New value for Prop0 from backend" + prop0); + + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SET_Prop0.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop0", new Enum0Parcelable(prop0)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Enum1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(Enum2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp3Changed(Enum3 prop3){ + Log.i(TAG, "New value for Prop3 from backend" + prop3); + + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SET_Prop3.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop3", new Enum3Parcelable(prop3)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig0(Enum0 param0){ + Log.i(TAG, "New singal for Sig0 = "+ " " + param0); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SIG_Sig0.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param0", new Enum0Parcelable(param0)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Enum1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(Enum2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param2); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param2", new Enum2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig3(Enum3 param3){ + Log.i(TAG, "New singal for Sig3 = "+ " " + param3); + Message msg = new Message(); + msg.what = EnumInterfaceMessageType.SIG_Sig3.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param3", new Enum3Parcelable(param3)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java new file mode 100644 index 0000000..46e0460 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbEnum.tbEnum_android_service; + +import tbEnum.tbEnum_android_service.IEnumInterfaceServiceFactory; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_impl.EnumInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton EnumInterfaceServiceFactory thread for the system. This is a thread for + * EnumInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class EnumInterfaceServiceFactory extends HandlerThread implements IEnumInterfaceServiceFactory +{ + private EnumInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static EnumInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: EnumInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractEnumInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new EnumInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new EnumInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final EnumInterfaceServiceFactory INSTANCE = createInstance(); + } + + private EnumInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static EnumInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + EnumInterfaceServiceFactory t = new EnumInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java new file mode 100644 index 0000000..86c51a2 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbEnum.tbEnum_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter; +import tbEnum.tbEnum_android_service.EnumInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbEnum.tbEnum_impl; . +public class EnumInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "EnumInterfaceStarter"; + + + + public static IEnumInterface start(Context context) { + stop(context); + androidService = new Intent(context, EnumInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + EnumInterfaceServiceFactory factory = EnumInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for EnumInterfaceServiceFactory"); + return EnumInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/IEnumInterfaceServiceFactory.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/IEnumInterfaceServiceFactory.java new file mode 100644 index 0000000..3ea7cfd --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/IEnumInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbEnum.tbEnum_android_service; +import tbEnum.tbEnum_api.IEnumInterface; + + +public interface IEnumInterfaceServiceFactory { + public IEnumInterface getServiceInstance(); +} diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..ca3911c --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java @@ -0,0 +1,577 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbEnum.tbEnum_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbEnum.tbEnum_api.TbEnumTestHelper; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; +import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; + + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_android_service.IEnumInterfaceServiceFactory; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IEnumInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class EnumInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private EnumInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IEnumInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IEnumInterface backendServiceMock = mock(IEnumInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IEnumInterfaceServiceFactory serviceFactory = mock(IEnumInterfaceServiceFactory.class); + private IEnumInterfaceMessageGetter clientMessagesStorage = mock(IEnumInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, EnumInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IEnumInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Enum0 initprop0 = Enum0.Value1; + when(backendServiceMock.getProp0()).thenReturn(initprop0); + Enum1 initprop1 = Enum1.Value2; + when(backendServiceMock.getProp1()).thenReturn(initprop1); + Enum2 initprop2 = Enum2.Value1; + when(backendServiceMock.getProp2()).thenReturn(initprop2); + Enum3 initprop3 = Enum3.Value2; + when(backendServiceMock.getProp3()).thenReturn(initprop3); + + + Message registerMsg = Message.obtain(null, EnumInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp0(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp3(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Enum0 receivedprop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + Enum3 receivedprop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + assertEquals(receivedprop0, initprop0); + assertEquals(receivedprop1, initprop1); + assertEquals(receivedprop2, initprop2); + assertEquals(receivedprop3, initprop3); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, EnumInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(EnumInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IEnumInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop0PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.PROP_Prop0.getValue()); + Bundle data = new Bundle(); + Enum0 testprop0 = Enum0.Value1; + data.putParcelable("prop0", new Enum0Parcelable(testprop0)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp0(testprop0); + + } + + @Test + public void whenNotifiedprop0() + { + Enum0 testprop0 = Enum0.Value1; + + testedAdapterAsEventListener.onProp0Changed(testprop0); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SET_Prop0.getValue(), response.what); + Bundle data = response.getData(); + + + Enum0 receivedprop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); + + assertEquals(receivedprop0, testprop0); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1(testprop1); + + } + + @Test + public void whenNotifiedprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + Enum2 testprop2 = Enum2.Value1; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2(testprop2); + + } + + @Test + public void whenNotifiedprop2() + { + Enum2 testprop2 = Enum2.Value1; + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + assertEquals(receivedprop2, testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop3PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.PROP_Prop3.getValue()); + Bundle data = new Bundle(); + Enum3 testprop3 = Enum3.Value2; + data.putParcelable("prop3", new Enum3Parcelable(testprop3)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp3(testprop3); + + } + + @Test + public void whenNotifiedprop3() + { + Enum3 testprop3 = Enum3.Value2; + + testedAdapterAsEventListener.onProp3Changed(testprop3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SET_Prop3.getValue(), response.what); + Bundle data = response.getData(); + + + Enum3 receivedprop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); + + assertEquals(receivedprop3, testprop3); + } + @Test + public void whenNotifiedsig0() + { + Enum0 testparam0 = Enum0.Value1; + + testedAdapterAsEventListener.onSig0(testparam0); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SIG_Sig0.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0 receivedparam0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); + assertEquals(receivedparam0, testparam0); +} + @Test + public void whenNotifiedsig1() + { + Enum1 testparam1 = Enum1.Value2; + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + Enum2 testparam2 = Enum2.Value1; + + testedAdapterAsEventListener.onSig2(testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedparam2, testparam2); +} + @Test + public void whenNotifiedsig3() + { + Enum3 testparam3 = Enum3.Value2; + + testedAdapterAsEventListener.onSig3(testparam3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.SIG_Sig3.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + Enum3 receivedparam3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); + assertEquals(receivedparam3, testparam3); +} + + + public void onfunc0Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func0Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum0 testparam0 = Enum0.Value1; + data.putParcelable("param0", new Enum0Parcelable(testparam0)); + Enum0 returnedValue = Enum0.Value1; + + + when(backendServiceMock.func0(testparam0)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func0(testparam0); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.RPC_Func0Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + Enum0 receivedByClient = resp_data.getParcelable("result", Enum0Parcelable.class).getEnum0(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func1(testparam1)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1(testparam1); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum2 testparam2 = Enum2.Value1; + data.putParcelable("param2", new Enum2Parcelable(testparam2)); + Enum2 returnedValue = Enum2.Value1; + + + when(backendServiceMock.func2(testparam2)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2(testparam2); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + Enum2 receivedByClient = resp_data.getParcelable("result", Enum2Parcelable.class).getEnum2(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc3Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, EnumInterfaceMessageType.RPC_Func3Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum3 testparam3 = Enum3.Value2; + data.putParcelable("param3", new Enum3Parcelable(testparam3)); + Enum3 returnedValue = Enum3.Value2; + + + when(backendServiceMock.func3(testparam3)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func3(testparam3); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EnumInterfaceMessageType.RPC_Func3Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + Enum3 receivedByClient = resp_data.getParcelable("result", Enum3Parcelable.class).getEnum3(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbEnum/tbEnum_api/additions.gradle b/goldenmaster/tbEnum/tbEnum_api/additions.gradle new file mode 100644 index 0000000..6dd8b2d --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'tbEnum.tbEnum_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_api/build.gradle b/goldenmaster/tbEnum/tbEnum_api/build.gradle new file mode 100644 index 0000000..1336787 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "tbEnum" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java new file mode 100644 index 0000000..4914482 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java @@ -0,0 +1,85 @@ +package tbEnum.tbEnum_api; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +//TODO imported/extern modules +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractEnumInterface implements IEnumInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IEnumInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IEnumInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp0Changed(Enum0 newValue) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onProp0Changed(newValue); + } + } + + @Override + public void fireProp1Changed(Enum1 newValue) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(Enum2 newValue) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireProp3Changed(Enum3 newValue) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onProp3Changed(newValue); + } + } + + @Override + public void fireSig0(Enum0 param0) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onSig0(param0); + } + } + + @Override + public void fireSig1(Enum1 param1) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(Enum2 param2) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onSig2(param2); + } + } + + @Override + public void fireSig3(Enum3 param3) { + for (IEnumInterfaceEventListener listener : listeners) { + listener.onSig3(param3); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IEnumInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum0.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum0.java new file mode 100644 index 0000000..66e2dba --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum0.java @@ -0,0 +1,30 @@ +package tbEnum.tbEnum_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum0 +{ + @JsonProperty("0") + Value0(0), + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2); + + private final int value; + + Enum0(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum0 fromValue(int value) { + for (Enum0 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum1.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum1.java new file mode 100644 index 0000000..e698d63 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum1.java @@ -0,0 +1,30 @@ +package tbEnum.tbEnum_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum1 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2), + @JsonProperty("3") + Value3(3); + + private final int value; + + Enum1(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum1 fromValue(int value) { + for (Enum1 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum2.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum2.java new file mode 100644 index 0000000..7daaf7b --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum2.java @@ -0,0 +1,30 @@ +package tbEnum.tbEnum_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum2 +{ + @JsonProperty("2") + Value2(2), + @JsonProperty("1") + Value1(1), + @JsonProperty("0") + Value0(0); + + private final int value; + + Enum2(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum2 fromValue(int value) { + for (Enum2 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum3.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum3.java new file mode 100644 index 0000000..a050167 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/Enum3.java @@ -0,0 +1,30 @@ +package tbEnum.tbEnum_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum3 +{ + @JsonProperty("3") + Value3(3), + @JsonProperty("2") + Value2(2), + @JsonProperty("1") + Value1(1); + + private final int value; + + Enum3(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum3 fromValue(int value) { + for (Enum3 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterface.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterface.java new file mode 100644 index 0000000..3f6be3f --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterface.java @@ -0,0 +1,48 @@ +package tbEnum.tbEnum_api; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; + +import java.util.concurrent.CompletableFuture; + + + public interface IEnumInterface { + // properties + void setProp0(Enum0 prop0); + Enum0 getProp0(); + void fireProp0Changed(Enum0 newValue); + + void setProp1(Enum1 prop1); + Enum1 getProp1(); + void fireProp1Changed(Enum1 newValue); + + void setProp2(Enum2 prop2); + Enum2 getProp2(); + void fireProp2Changed(Enum2 newValue); + + void setProp3(Enum3 prop3); + Enum3 getProp3(); + void fireProp3Changed(Enum3 newValue); + + // methods + Enum0 func0(Enum0 param0); + CompletableFuture func0Async(Enum0 param0); + Enum1 func1(Enum1 param1); + CompletableFuture func1Async(Enum1 param1); + Enum2 func2(Enum2 param2); + CompletableFuture func2Async(Enum2 param2); + Enum3 func3(Enum3 param3); + CompletableFuture func3Async(Enum3 param3); + public void fireSig0(Enum0 param0); + public void fireSig1(Enum1 param1); + public void fireSig2(Enum2 param2); + public void fireSig3(Enum3 param3); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IEnumInterfaceEventListener listener); + void removeEventListener(IEnumInterfaceEventListener listener); + } diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterfaceEventListener.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterfaceEventListener.java new file mode 100644 index 0000000..87eedfe --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/IEnumInterfaceEventListener.java @@ -0,0 +1,17 @@ +package tbEnum.tbEnum_api; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; + + public interface IEnumInterfaceEventListener { + void onProp0Changed(Enum0 newValue); + void onProp1Changed(Enum1 newValue); + void onProp2Changed(Enum2 newValue); + void onProp3Changed(Enum3 newValue); + void onSig0(Enum0 param0); + void onSig1(Enum1 param1); + void onSig2(Enum2 param2); + void onSig3(Enum3 param3); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java new file mode 100644 index 0000000..c8f708b --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java @@ -0,0 +1,11 @@ +package tbEnum.tbEnum_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class TbEnumTestHelper +{ + +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/build.gradle b/goldenmaster/tbEnum/tbEnum_client_example/build.gradle new file mode 100644 index 0000000..8d0ebba --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbEnum.tbEnum_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbEnum.tbEnum_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbEnum_api') + implementation project(':tbEnum_android_messenger') + implementation project(':tbEnum_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6b4b66f --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java new file mode 100644 index 0000000..904d9aa --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java @@ -0,0 +1,358 @@ +package tbEnum.tbEnum_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbEnum.tbEnum_android_client.EnumInterfaceClient; + +//import message type and parcelabe types +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; +import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbEnumTestClientApp extends Activity implements IEnumInterfaceEventListener +{ + + private static final String TAG = "TbEnumTestClientApp"; + + private EnumInterfaceClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbEnum.tbEnumserviceexample.TbEnumTestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp0 = new Button(this); + bProp0.setText("Set prop0"); + bProp0.setBackgroundColor(Color.GREEN); + + bProp0.setOnClickListener(v -> { + Enum0 newProp0 = mClient.getProp0(); + + //TODO increment + Log.i(TAG, "SET prop0" + newProp0); + mClient.setProp0(newProp0); + }); + propertyButtonsLine.addView(bProp0); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + Enum1 newProp1 = mClient.getProp1(); + + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mClient.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + Button bProp2 = new Button(this); + bProp2.setText("Set prop2"); + bProp2.setBackgroundColor(Color.GREEN); + + bProp2.setOnClickListener(v -> { + Enum2 newProp2 = mClient.getProp2(); + + //TODO increment + Log.i(TAG, "SET prop2" + newProp2); + mClient.setProp2(newProp2); + }); + propertyButtonsLine.addView(bProp2); + Button bProp3 = new Button(this); + bProp3.setText("Set prop3"); + bProp3.setBackgroundColor(Color.GREEN); + + bProp3.setOnClickListener(v -> { + Enum3 newProp3 = mClient.getProp3(); + + //TODO increment + Log.i(TAG, "SET prop3" + newProp3); + mClient.setProp3(newProp3); + }); + propertyButtonsLine.addView(bProp3); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bFunc0 = new Button(this); + bFunc0.setText("func0"); + + bFunc0.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func0 "); + Enum0 param0 = Enum0.Value1; + CompletableFuture method_res + = mClient.func0Async(param0).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func0 result "+ i); + return i; + }); + }); + bFunc0.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc0); + Button bFunc1 = new Button(this); + bFunc1.setText("func1"); + + bFunc1.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func1 "); + Enum1 param1 = Enum1.Value2; + CompletableFuture method_res + = mClient.func1Async(param1).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func1 result "+ i); + return i; + }); + }); + bFunc1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc1); + Button bFunc2 = new Button(this); + bFunc2.setText("func2"); + + bFunc2.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func2 "); + Enum2 param2 = Enum2.Value1; + CompletableFuture method_res + = mClient.func2Async(param2).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func2 result "+ i); + return i; + }); + }); + bFunc2.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc2); + Button bFunc3 = new Button(this); + bFunc3.setText("func3"); + + bFunc3.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func3 "); + Enum3 param3 = Enum3.Value2; + CompletableFuture method_res + = mClient.func3Async(param3).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func3 result "+ i); + return i; + }); + }); + bFunc3.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc3); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new EnumInterfaceClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onProp0Changed(Enum0 newValue) + { + outputTextViewProp.setText("Property from service: prop0 " + newValue); + Log.w(TAG, "Property from service: prop0 " + newValue); + } + @Override + public void onProp1Changed(Enum1 newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + outputTextViewProp.setText("Property from service: prop2 " + newValue); + Log.w(TAG, "Property from service: prop2 " + newValue); + } + @Override + public void onProp3Changed(Enum3 newValue) + { + outputTextViewProp.setText("Property from service: prop3 " + newValue); + Log.w(TAG, "Property from service: prop3 " + newValue); + } + @Override + public void onSig0(Enum0 param0) + { + String text = "Signal sig0 "+ " " + param0; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig1(Enum1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig2(Enum2 param2) + { + String text = "Signal sig2 "+ " " + param2; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig3(Enum3 param3) + { + String text = "Signal sig3 "+ " " + param3; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} zs4%E?srLW`CJh0GCIK@hTkrW7A15Iu%N&?Q^$0+!{Tv&|t^Y@u%!L zglTg&?Q5q#ijZ;&HBQ?FNPp;k3J5!&{^+SGq?AX~SiOM9jJMRpyP?RCr@z38AQyy&WRMaC;n4una$~nJKSp?q|s8F00c9?Q! zY_ovvjTFm+DeQM^LXJ#v0}6HRt3R1%5PT*}W!k8BEM;Jrj8dIceFo2fhzTqaB3KKk zGlCLI)gU25(#u6ch6GeB1k@eHq7l{EHXv0n6xE#ws#ri}08kkCf8hUt{|Ejb`2YW* zvg}0nSSX1m=76s?sZhRY$K=3dpJ+y*eDULGnL2}4>4nvW^7_<~wIM_5fjvwt4h1|g z)g0Z6ZFq9j<~9~b8((~TN{Z?ZQfw|is&Xp~AC61sj;xItKyCHdI|tCMC_LbXF>~vR z=w6V3^H=W4CbAgR4#xw}ETTwu2guW~=Crl@SMXv85jQ=%y!s^?m4PI0My7MWICO;- z175jm%&PcPWh8QdOU(#8bp4!N7ET-+)N}N2zk2)8ch|4Q&lPFNQgT-thu053`r*h3 z_8dI@G;`zn;lH$zX3RzIk`E8~`J=BBdR}qD%n@vVG1834)!pS1Y?zVkJGtsa(sB~y zNfMYKsOJb%5J(0ivK8d+l2D2y&5X!cg3BG!AJ}910|_${nF}sC1QF^nLIhzXk-Y#x z0)&1iK!O;Og0Ky!;`b~v%b$`S4E&fB)1NB4v@8wr( z&+NX4e^&o)ecb=)dd~C!{(1e6t?&9j{l8%U*k4)?`(L3;Qjw z#w7FS+U(94MaJKS!J9O8^$)36_J8;thW#2$y9i{bB{?M{QS_inZIJ!jwqAbfXYVd$ zQ5fC$6Nc9hFi8m^;oI-%C#BS|c8vy+@{jx6hFcf^_;2VRgkoN(0h!_VSGmgNPRsxI z8$rTo0LaYq-H5i&gtj81=&xU?H-Y2==G@uQV7E`@+2E9XQW@{&j`?EOktk|Ho{HU>ZqDzvgjwBmdex z&uZNd2C1h{{}2k6Ys9$*nFP3;K%u!MhW`uZy7Sn`1M1zs@Es&;z*Z>Gsh@-3Fe6pE zQD2@cqF((NrRevgvLsvM_8;;iNyJ5nyPyy?e!kvKjGj`6diRFBEe49Oa7wwkJFV7Z z$YT&DWloYu-H?3<0BKn9L&JYDT-SK~*6c5pi18P26$JESKRYj{T7Zk6KiRJcbvOO*{P56Q6s8msbeI3>|j>K9}Q9UBeq*inXKemCm`-<5|-$ZyN4u$(3 z&HcvqehFD%5Yrmykg-^d`=BSa8(i=>ZoC77^mWY{evp(km@aHqhUECBz76YiR+VYK zY_avFC~V3$=`6C4JhfHAQ@DZtUOwH`L;oYX6zK0-uI^?hS$ALfq}A7evR;ohJHij} zHSZdW?EKv9U1s4oD*<(0oQ*;MaQ6@cvGL zuHCPgm_NhVsgp^sfr*ia^Db}swo1?O(_Q2)y+S$CBm+g=9wCOUPbz(x)_GbaKa@A7 zuI&!ynLiZRT#V%_y_-D`0Z5lT*auoe{(U5NylTzFSJW()W-#F6*&A`LNO1bV#Y;QJ zSbLBnp|B^dtK|KIWC|No>JjWBWE@n7O)x{&^E(WMeMvp57#qA8m* zeTow*U@_86B#Fm*rxyYu5PRWaWHx8y> z*qmHEp(AMDl0v)ij(AY8fnH=~ZwwjVAbu*m5;xPfidh@ov6d8g zfJsi&!QyK53Es%sC39ts;54V68koALD4b|%tNHW0bIkZAJKa=W&FomJSEDT>W1xIX z1x%Z>AvNIsSPLcn3RTcHXb@KB?cuM)=x6fcIx>&(GxqZ8w3p#jJ(GVgc*`c0HG}dv zIop&Qim!K1NFwic%07KcjWgHBPUkq7f~lj;TPqVGTiT#cUeim>;nY`>h@a*S{qQex zQ`z62WK|Mj)Y{tfF{;T4P;c8$Q|KU?Joh zIkA^z%X7z|r>4aTh@|StTi!-r1D!g=zb#3d#{{&K3CqE$Iz-UH<%37c zRfkO`&uM%#AD3PHv`g5t0e^O%nVL0d{Xlx^EjEC3#skF@`zl-7PF^0oxW)1!C!JxR zWvuAHH?)61FKA1QeT*_sY7;_Id#!GmV4n`MO{~sv}VLSK` zXRw=Y=Clz*00B(5y^K;gCZMAzjT5+c3IC=)l(9VIDdatpxj3y89WwI|bH&$!ZEvp` zPR!T@#!(|KfI-w?!&+7$N3F6>tD{YO4Qg$d_`nNEdfVCha9vaPn0jI0`)`@*72hq! zpU5ND^P*RoEkbD5o#az(-g=Y)L>HH>Oc%}$ zT3Rs_ih0;4+Lv4Y;@Iv(;fUbQ=i-G(#>vghec~*j(I#r|5mqFiJBpzi&hzEcD{u$< zRsm0BVYn=pT;0>R(itW|*D&;O%bOc7et9ACaH#J>z3A1A~6fdP>pmbM%xzm4>|;c_?B+%sl;Qs2{t!60$^u zH1t@9^6>;?!FuusnISi$f5CL&;z?EqJN$FBuWDA#D5`cy_UvCFIVvf{c?4N0teh;d zET$7aVbj08KTQS!x?Nd1Is8q8qFzs}a=!@nJ;7FSfCY^T@D-gpw`w<6e#X3+;O}1h z$%I!M)0bg|EKUA04Qjn@+x{Rj8vt6Wn!R|3A92z}^$KfF5(#CWr4y#~re1CN4i4w0 z#GsypBR{xA3Er7sgAi(|}1-W?s~n$7?K|9WL8kpVfw-;#b9 z+mn;=ep!162U5R>_t}fOt~tE?s#m( zO-S$7>Ay6*hHdZ)7_oU915WYYCIX;hFI-U2EWYX!pllONr@Q--2o~`!isi6vTPLJ4@(|o=%NHYjo0_S&q*UQIROw@*N-By@PaQ&;YxFZ0aR zX&}LeOEz);#m~Hwm^VAY8DK}b$F4bo{jMN?d!lxKPhNklzr^Cd`0f4oJr^z=I|l`* zm8AHm*fPV`0=lF3Pnnp}&J0N1X@}-D94YvmUabFrLGSnTz7Mu^21F#O5tN#CuY9Vh zUZBH=ez%h*wkf0hBtXJh1SN3d+IF{gzT7lp)j}n?03lt;XSQRAh7qd&v;RwTYDuQ# zbI2*r<>?x-G0@hM{;%{VBD7nLKt~D`T~-HAt5;h%i0_=Ifs=yHma5dhJ+QMG?Ux(a z|E?1CMy1!~oA`FP!k~iG=t&5#>bVdz=peT8HMB6Y)#7PpETtNryT^+Rv3vpJaF^zP z{H}0-LyV9Fu21ID%wO9f1IKlFr1p4c{o-?03vyB-tr5duk^&L$;m_|f$vs`^Sl{j2 z95}oY{LlY+=ZS%J+tZoXCd0*sSU7w^gjovXn+g7uyra5{cU49@yHf#Z^Jl-$9cIfo z+AJuxH$VLb=#+uBbVmUjnx zxb1pZ@-O9=AIk4@S)m6fJ2?{HrNYwwnL3a45muuNjr;6$O`bGEM0T4A2_S$t=86*- zcO+0mywg*j#A4mU}enR_!cGmIYQ;qwfchWtFEXL)AK%*;=j znYne+hS4EMy3S)C*mZ1KI>!+)0V@9!N6H$Y}~MJ{rYuf zz^KljIWvFi-?#?V@LPR&c6Nn{!=XM z>}-h$S76;$H{E{Y%@^zlmOl^efBwa%UU+jJD9UVukQ3ti_kH-?H*RC0?M1W%FCvMB zM_+v6fk$6X2sx)-p~B3&Kl{nscK}pNLM*qjtpaf9>AU{-iPKQZR8yCg!TY}Qg*(;) z)gdvCcB%kppZc$VdvsK@)3l1{&DG!d_6OHOS`y=ITLEVu`unSKA2E%JD*DVX{LJ}K z9l>hMRDqxQh0lnpGHpVYneX}eA3Pt|2v%=q;rt)``R|#bDyB)OXY&vI_@|*}h}G?^ z@aZ4_!7cQPX`!fW_?{oT1NTwHs#l5L-0`E|y@48<3Q^HFf8=Idi zpJYD%1MkII!~|7I^WGo)IF=?{>ACnjJ_WUi39C}!Q{QnheVJqeKKqq5^o5CBde(g9 zvw$X6^jz_^E2$wSw4!q5*RG(C2_^XO$HBn_55vbl44OnTTRwRaePP0vo{K)U1#99& z<>rq7V&V(<&@I%MFoN5zrY}sz=(*-L&}1QQ*a%`u25h{cFj===17eB_uGuzG&byQ< zrm8BJZl4r_E$3k|Wo6FW0-6M7>qac5uFQsQcmkLWGfeH74S3Z_rJ!jgN++!@i=HW8 zkyjI(oPH-+-N#Qc^-mpNO`bc6r=2-<%&Wy5K1vfFJB(L_IkpS6fY^NmuL8qsgj>MD zn~BHH9WM~32_3vd=W&B)k7F9q%stJx+b_L_X-4zr^LVUMCmyCTA3sWtkvsmME?Xiy z?xOSfB=_$oY06~J-HcCq&)qcW{j;uP;?Dm}=hkq?zh&n!;m((-G-u_t|6x399Q;>A zgNpxoJNj{u|MFDH7Rhq@FCAl0dE|ddnl!oh9{Lq?@JDoR6L;C941IK`ISfdE$4S zE0AUQ8+2|Ncl_q5QkSp#AODp~(^mfP&%Au@@|TBQwoP`UU+V{6u8|)6ZA{~uKmQ*M zmrMTDU8S~8Eqi{^v0Ug&5Upcm#y7Z1(RbgZAG8jB$eRwCspQ)>5;U)oGZ&E5aeR*K z8Yt`Y0$G))Yd(Y3KH}tA4`-_QmNke5hU_|nq=xtyjwW(_o?itz>B>WM&^63bNdQ)k@-IgDHW*RW$Xo9#RzrTrCn7L2H{9Amq|qNg@#eZY=|P zCoI?2s+L)zsM%WX(NbVEY^`C>lFjIBYmJ6@DKJ0ZT4&F&WHW!dwa%QzOG!?jY_2(S zDcEzZbz*2Q!43|z))9yOP9X1Xt%DXzwY(3tl-TR=Qb_MbZYRrooh;dYYmS!U_as1(=YVB?Q_A|tNu5Ut&_q3jbfDM zoFxT^uEuH`nX3*sB%K?GuHUkweYReBwnHqh3P)~`+s3+Tj!rDA1e)8vuBv5J*IsxC zkd^~b(aGzArj08{>cnzOuy04C+C`}gb|Yz-1avxeWzev3NzcHbz_&4W@QCr$z3~w=8Ua- z`;vfG1~BP8CyLb=F7t1am~ph_#|O%$khSJ9%Vtcn)YmpgQxF?xM^_Vb+5fnpB^W0I`f%X8gb9#X{Q-yJG0{Z56aWeI&zPxnf5pdJA38bM`cYnS#x)% z`n1tFf$i)W-hGm(f9mde^=X@NcV_lFb=P`4&CI&H=IArijGwdCk&X@uQ$5xmj!~^? z#$ROCI)V-~t%L%GS#wo@U27ddR`4`3)WoB{R-4snfNrfee|kI8^bu#yDgYqOwas9# zmcb`3!kRJ`Cr=_tq)8aMt{aGtUZsqwVlj6DgCGre>AEt&x8H_in!x@uwgExIh|-mA zjdaC(29~CTVSaaF7HPbql&*9Uo8P@f)>LqCXclr}peS7_1BQ28u9PO8Eq1@`l3q9o zkfKCaO2?T?ZyA6loW<#9_c^O=m<&h}CA!ineAD@=(gbq`vyT|tiJ6#^B1$P;;qax` z55k&Q?wEh#87niLo*+n4L@65J(Nz~=Ya%7^(miLb(E>A3B@|Jjl;FU&D>o|9#7PJH z?|ago!o;WC^h=|T7PVBg(DAB}72cyUS zb(f>Bwbr!F1eTCO5fpj<{PqhY5>143p?~5ZA5H40);=@M#MYvrB6gqHbU_!GSY??i z%s=>-ciA4*zOOZHds0a(kWewZ4h(k8h(ua7HX)Au&mY~H8KY6(_cb$_&fA@QjIW-*heP3%$d!m5^AdnT}`12qA^c@!g3DOwZ5WwE2?)-yU z!)Vx#Mtxt?FzFTwK!77sy7)sMzUd->w4^bxtpM2j!b1pjgyk zGKwWGeb4)^zjy{9Es&PU1}gwg?|J#L$KJB7ett9@4M%-nGtIQr0>Fl@8-yh`-+1ed zS6r}(MeSvgSoFmH*_WPu@i?}!AB~2?;i&IxrkNg~cQ9Som98tcq)k^|eeER|Zl77t za-TVUc;DNvzVXJ%w52+#weN?+;i#{f#!Oc&z?81*N>^e~ltRS%ZI@lR{rs()HmqG! zx*}ZrI-EZ}ckJMiy>A^oofwDfC~IH)z8{VHKGT@#E5I(Ll&+MnMCl>~AV7+>Gi%mF zkU1QlKASdR0B80!YhP<$Ywi0?W2Ux45oPfxv9QolWzJPD^weBfvo4SONxP35106sAmh(e+vAs0GboFD@PvNs)jNPvarhW}0YliZEg{Gazv z+JDIpoojRVPr<*C|BTq<`6ga{5q^8^!|0cxe=rZ!zxH3%f5ZO0cQ*Z<^$Yt2{|Ek0 zyT|*F+CO@K;(owBKtGg!S^xj-Z~rga2m6nxKl9J=fBSuNKW_dLKWhJKeg^-Xe`^1? z`TyJj)8E!#>_3Y?uKrwqq3LJ#SGU>AzUO|6`nR^u&3FNN_jGOc zw)Nw`wr3yIKhgcee6IaN=ws>M{6677%)hPwx&HzC(f&u~&)6@b2kNRzBDQAP0*H73 zq%McOmRk{B3i47qRe=DA*$&odrbEJZ*pV9XXa&p@wlW~@Yfs>V{yiTtplMhgM*-Bz zsSnlq&pG;z0OUN%$~$3=g1UF+G*>+17eRbBf3=y79J}KR8owon@$1Z7MIrvvWWH)34nK2SD)GsrJ{l z1Cl#oVo3A8qY3e=aF)qzms~FG#2$LzT=gs&aVMOj>(%{y<&O0cG!nCiESl~x=^dF{ zKvj8F1K8Ng171wwM5Fh4KoQw`_c6#y$(5cAm7e}~nJ#A*fx+c9;y#&W!#VukR)ugk zKp3=+;Ut+IYn%m+r4d*<`L2h%aDnX5}^!5R|H;(34AoVWjRx(msBZvk;rCI*|~ zdOijqI@9Z{Vu!~jvHW{lBa$rnl4+!s_5sfK3bCGk-B%iDe&@-}+%fOKU|(9?V1 zHE8&@4z)Kx!RAvAs z!Wic9=o#(bg?kc-G68-m(jZ`^=XGUXb)}t(%&~sjFnV^sEX%hSy6UKC4iOhgV=BHV z2w`4g7Y=s#Vu2B_?#VQ|hP39@eArgfX>-0S+dd&^mx0*wp}>)x;c4RUgxz%;oNe?& z-7-lJ@Y^2^C;=qJsxx5|xF)*pTGhch2B&kxtn;f!7=gznk}I3}Dh}(CoMXgA5-p&kS202!l?!fT3t|HG*rIP~mS* z$Wjo}jq3}z$Qq!9yrtd3fM0N629ZM?LU$nv@Tv9b7I;D|;0H2dsA~g7Z7zp1| zB)XmrkMgF6OQr|R)HHD^TE{Y#j!~SR?b`Xt3Qs`B+x<hxexYeAjMUWdZ-*n9%(1)Wb(n2U<><7&9dwGJmrob)4%H? zlQ%z+L-^$dFhhH|@u$%97Qz?*Ynh2VG@q|?8vY&L74&fs&_b&3$x&Oyjl~LQDRRap zJU4U*R+(2Dd!G+lh8!V{pT_UJn+^1Qg6$` zqkNm(a#hWyc6SP+p5=C4HL8-m`pO`5o~`-LI?_h5CsH?F_%?nDodmz&pWR20WTpJE z?N|wSzLjMUK8E)a2tI}Lf;+;*M|h3Y(U#>)g1>zk9|Hd}oZAa2 zLYBWBoSW!Ts!RwXr^8h+U*@{9{zqS^iH)Op<;r`Uw~nc}<^$V~_i%$GFjaG?X1@E|M`h)nekvFKt`Dh-f>@|0-`Xoq)o` zx;JmzDfOV9qCx|EVpogEe0LK~tGS?5$$L_i6P$P6wIsCQaP_;d{{N=iV@+8LI}o#( zvo*Ejy=IIn{rdIQh1&q-{EuohpVOjJ^Q3lD*YTp37$^RRgn8ihpdu5{Ct%5-KO!VL zcNB6dUajXI9jkm-P|i3~GB-A(X`P1Oqqb$tcku)UJw0w3GeUijb__#QT4j%64z%EeB7S?jlWwx_7&+EEvB|6N=kV}DwnyAlX=?j`) zmU#!$*^@NIu#n_d7;WoJV@*Fbv9|yJO4;n|BNF2xy(54RyB>t~8lUOUW$&2%Nwi1y zx6JxW88>U2$#qhl^6KUbtmg9}D0o5vYDT7kWJthLGkpGnN4T>{St^_EU>4;DmLF9o zr|LqsA8_MoNLQ=}w?8u!ziSZ@PC#Y<#9uJFo-ozVo6D;<8j^1$c|qAE3ZTE5i~zmE z$BU5lw6l=EWsg^y^;8>r9qH{xfL|~PZYK#md$zZ0?o11gV<*WSW~cgy2GYGQir%wf zt4iW8D+;s*;RGrmd(-T<@2&j(Cb9xhV*l-x`TpK`xq|7p?5R%5*s!69?2c!cC*VY* z2DE^9pvOPLU!1e}wA8S8opcTJ3`NB>hY=JQnL~QFXR4K8A$BqJnoEB$wn-%u@E6Mh zCfMF4kusv3N!(aHC}4)Xs^xoOwXd%e^6pi5|DZo=Q25j+6HlJ^7FodH6y1bMROR^q zGu6)fopS`h%Sw<;ZH%TEPf+#81-#_v+@8nlR0jLcIDKQtLleOC)6yLZgC!D9X3GgS zohwU{v$jl=quD#Go^hB{`@Qw*a%`(^jyT~=q^bWgGzRj;|12J55HWdCWV}EB|K=%N z3Nq-qxJJ`>^|1MNN+q}zTB&ooE3j==AgK@^UW<^oSbeALa2peF)Th6{@sj0KyMNHZ zksk1+MXN2tv+22A%cQOGpS9)77(uP9mh+!5T5ERLvF@b}$+WvXM45Z?-kCa)fb~f1 znVbTD$Gx-0Zxc`0D@YgHakge6SL0H`-vN_x?AP0>iGH0_EE&=v83hMJgaKAI0jJXm zVxVz;X<$v6WW7}fxROO7vr#YLP;;lij5VrX{;>7kK6TtOH&6|Ar^xo>00%+u$C4@# z>!jOt6*3><171+WxoZnKDTzJtDRw+T030;yI}~uV@9fCnei^I*j>Bp&mzP2d=FPb_ zCM*l_+$LDR3B*a!A$g#>xsrZvw0lckxmMg>0aQd7tPyN=t{dgXb;Ie+T8{fZH=gdu zM7Rg9c(kg(Jg0?ARRRl=AONFKrvFj)lTY$KfT%6^6s`mk*ABGhsce*LsoD>K{z_M2 ziPpnu+lw22PfF!CoId^6n*G4H(Ix+#+N{C(da7t1BYMGEaE#PdpOLxsVD5riQXHp@OX;`S`8VnpM~)I920w~<3|mo0 zf8~Az`*?2?H&gZ&*K&bRkV@qzvMlRHXys8*Ze2+1c?5o!^+$&MHxB@4Ee5cke52R! zmn7AZtY6ST%ixgU5)%$%QcwHj7Es-Qu^kLAPwy%7pGBw_4Q9#da^W2$}axNHr03)_nw z5?yuNmXrI5HgS46)c5&}B)Tts49oU92>3xBLLy}FMUW=84DQbVq^;7_e7|(Sdz|&J z73N+M`rc2rt*oSWu#7S{*s~nH6HRHJS1SmzeXk|;CA)FI4bat3<%}nkB%;;?=F>B7ms9QSxv#@+69;@>QaR?REYX4&)=itG>rM{<{A79Rmk)`5ON#GL`*KX%}Ihk3w(RtM-WLt z?f&FLF}4N^yE!(pZ&Yj&Bc`~K0@4_}*0Om?wN|}4WJ>WL;G^H2*QpgEkGA~OET-Km zkwz|5{6dnz1U<2Pe9DNL>3g5FEIvp1jzP&2K#z~j%g6!7B;^zF+o95?fV{3mnB8*RMhCDNp>Am-3e@jNfMj?jHV$MWjk!DDKP zkAz$Y?Sr)!GUOX}qTQ5aMh|wq1uq}~joWyKl=b_LboM#wi{CMuz5x6BKlA-qy++cM01D3b7`uD z#l6M4pI;JCypO8JZ6?U&wNxR!{4oB_ zlV!x9+-&Qy6{%MQ{~yoZGkKiTSC`YS_j22~G;xUV855g2&C(zm^V!(wpcm@zn{%!g z4}JGo(sGZ1O~to-}le

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/colors.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/strings.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..cea8eca --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbEnumTestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/themes.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_impl/additions.gradle b/goldenmaster/tbEnum/tbEnum_impl/additions.gradle new file mode 100644 index 0000000..84a2786 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbEnum.tbEnum_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_impl/build.gradle b/goldenmaster/tbEnum/tbEnum_impl/build.gradle new file mode 100644 index 0000000..7bd9944 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbEnum" +version = "1.0.0" + +android { + namespace 'tbEnum.tbEnum_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbEnum_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java b/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java new file mode 100644 index 0000000..776d4dc --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java @@ -0,0 +1,221 @@ +package tbEnum.tbEnum_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class EnumInterfaceService extends AbstractEnumInterface { + + private final static String TAG = "EnumInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Enum0 m_prop0 = Enum0.Value0; + private Enum1 m_prop1 = Enum1.Value1; + private Enum2 m_prop2 = Enum2.Value2; + private Enum3 m_prop3 = Enum3.Value3; + + public EnumInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp0(Enum0 prop0) + { + Log.i(TAG, "request setProp0 called "); + if (m_prop0 != prop0) + { + m_prop0 = prop0; + onProp0Changed(m_prop0); + } + + } + + @Override + public Enum0 getProp0() + { + Log.i(TAG, "request getProp0 called,"); + return m_prop0; + } + + + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + @Override + public void setProp3(Enum3 prop3) + { + Log.i(TAG, "request setProp3 called "); + if (m_prop3 != prop3) + { + m_prop3 = prop3; + onProp3Changed(m_prop3); + } + + } + + @Override + public Enum3 getProp3() + { + Log.i(TAG, "request getProp3 called,"); + return m_prop3; + } + + + // methods + + @Override + public Enum0 func0(Enum0 param0) { + Log.w(TAG, "request method func0 called, returnig default"); + return Enum0.Value0; + } + + @Override + public CompletableFuture func0Async(Enum0 param0) { + return CompletableFuture.supplyAsync( + () -> {return func0(param0); }, + executor); + } + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Enum2 func2(Enum2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return Enum2.Value2; + } + + @Override + public CompletableFuture func2Async(Enum2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param2); }, + executor); + } + + @Override + public Enum3 func3(Enum3 param3) { + Log.w(TAG, "request method func3 called, returnig default"); + return Enum3.Value3; + } + + @Override + public CompletableFuture func3Async(Enum3 param3) { + return CompletableFuture.supplyAsync( + () -> {return func3(param3); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp0Changed(Enum0 newValue) + { + Log.i(TAG, "onProp0Changed, will pass notification to all listeners"); + fireProp0Changed(newValue); + } + private void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + private void onProp3Changed(Enum3 newValue) + { + Log.i(TAG, "onProp3Changed, will pass notification to all listeners"); + fireProp3Changed(newValue); + } + public void onSig0(Enum0 param0) + { + Log.i(TAG, "onSig0, will pass notification to all listeners"); + fireSig0(param0); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Enum2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param2); + } + public void onSig3(Enum3 param3) + { + Log.i(TAG, "onSig3, will pass notification to all listeners"); + fireSig3(param3); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java new file mode 100644 index 0000000..114d67b --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java @@ -0,0 +1,256 @@ +package tbEnum.tbEnumjniclient; + +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; + +import tbEnum.tbEnum_android_client.EnumInterfaceClient; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class EnumInterfaceJniClient extends AbstractEnumInterface implements IEnumInterfaceEventListener +{ + + private static final String TAG = "EnumInterfaceJniClient"; + + private EnumInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbEnum.tbEnumjniservice.EnumInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp0(Enum0 prop0) + { + Log.i(TAG, "got request from ue, setProp0" + (prop0)); + mMessengerClient.setProp0(prop0); + } + @Override + public Enum0 getProp0() + { + Log.i(TAG, "got request from ue, getProp0"); + return mMessengerClient.getProp0(); + } + + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Enum1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public Enum2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + @Override + public void setProp3(Enum3 prop3) + { + Log.i(TAG, "got request from ue, setProp3" + (prop3)); + mMessengerClient.setProp3(prop3); + } + @Override + public Enum3 getProp3() + { + Log.i(TAG, "got request from ue, getProp3"); + return mMessengerClient.getProp3(); + } + + public Enum0 func0(Enum0 param0) + { + Log.v(TAG, "Blocking callfunc0 - should not be used "); + return mMessengerClient.func0(param0); + } + + public void func0Async(String callId, Enum0 param0){ + Log.v(TAG, "non blocking call func0 "); + mMessengerClient.func0Async(param0).thenAccept(i -> { + nativeOnFunc0Result(i, callId);}); + } + + //Should not be called directly, use func0Async(String callId, Enum0 param0) + public CompletableFuture func0Async(Enum0 param0) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func0Async(param0); + } + public Enum1 func1(Enum1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Enum1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Enum1 param1) + public CompletableFuture func1Async(Enum1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public Enum2 func2(Enum2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param2); + } + + public void func2Async(String callId, Enum2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, Enum2 param2) + public CompletableFuture func2Async(Enum2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param2); + } + public Enum3 func3(Enum3 param3) + { + Log.v(TAG, "Blocking callfunc3 - should not be used "); + return mMessengerClient.func3(param3); + } + + public void func3Async(String callId, Enum3 param3){ + Log.v(TAG, "non blocking call func3 "); + mMessengerClient.func3Async(param3).thenAccept(i -> { + nativeOnFunc3Result(i, callId);}); + } + + //Should not be called directly, use func3Async(String callId, Enum3 param3) + public CompletableFuture func3Async(Enum3 param3) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func3Async(param3); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new EnumInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp0Changed(Enum0 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp0Changed(newValue); + } + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onProp3Changed(Enum3 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp3Changed(newValue); + } + @Override + public void onSig0(Enum0 param0) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig0 "+ " " + param0); + nativeOnSig0(param0); + } + @Override + public void onSig1(Enum1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Enum2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param2); + nativeOnSig2(param2); + } + @Override + public void onSig3(Enum3 param3) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param3); + nativeOnSig3(param3); + } + private native void nativeOnProp0Changed(Enum0 prop0); + private native void nativeOnProp1Changed(Enum1 prop1); + private native void nativeOnProp2Changed(Enum2 prop2); + private native void nativeOnProp3Changed(Enum3 prop3); + private native void nativeOnSig0(Enum0 param0); + private native void nativeOnSig1(Enum1 param1); + private native void nativeOnSig2(Enum2 param2); + private native void nativeOnSig3(Enum3 param3); + private native void nativeOnFunc0Result(Enum0 result, String callId); + private native void nativeOnFunc1Result(Enum1 result, String callId); + private native void nativeOnFunc2Result(Enum2 result, String callId); + private native void nativeOnFunc3Result(Enum3 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java new file mode 100644 index 0000000..be5c853 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java @@ -0,0 +1,221 @@ +package tbEnum.tbEnumjniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class EnumInterfaceJniService extends AbstractEnumInterface { + + + private final static String TAG = "EnumInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public EnumInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp0(Enum0 prop0) + { + Log.i(TAG, "request setProp0 called, will call native "); + nativeSetProp0(prop0); + } + + @Override + public Enum0 getProp0() + { + Log.i(TAG, "request getProp0 called, will call native "); + return nativeGetProp0(); + } + + + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + @Override + public void setProp3(Enum3 prop3) + { + Log.i(TAG, "request setProp3 called, will call native "); + nativeSetProp3(prop3); + } + + @Override + public Enum3 getProp3() + { + Log.i(TAG, "request getProp3 called, will call native "); + return nativeGetProp3(); + } + + + // methods + + @Override + public Enum0 func0(Enum0 param0) { + Log.w(TAG, "request method func0 called, will call native"); + return nativeFunc0(param0); + } + + @Override + public CompletableFuture func0Async(Enum0 param0) { + return CompletableFuture.supplyAsync( + () -> {return func0(param0); }, + executor); + } + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Enum2 func2(Enum2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param2); + } + + @Override + public CompletableFuture func2Async(Enum2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param2); }, + executor); + } + + @Override + public Enum3 func3(Enum3 param3) { + Log.w(TAG, "request method func3 called, will call native"); + return nativeFunc3(param3); + } + + @Override + public CompletableFuture func3Async(Enum3 param3) { + return CompletableFuture.supplyAsync( + () -> {return func3(param3); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp0(Enum0 prop0); + private native Enum0 nativeGetProp0(); + + private native void nativeSetProp1(Enum1 prop1); + private native Enum1 nativeGetProp1(); + + private native void nativeSetProp2(Enum2 prop2); + private native Enum2 nativeGetProp2(); + + private native void nativeSetProp3(Enum3 prop3); + private native Enum3 nativeGetProp3(); + + // methods + private native Enum0 nativeFunc0(Enum0 param0); + private native Enum1 nativeFunc1(Enum1 param1); + private native Enum2 nativeFunc2(Enum2 param2); + private native Enum3 nativeFunc3(Enum3 param3); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp0Changed(Enum0 newValue) + { + Log.i(TAG, "onProp0Changed, will pass notification to all listeners"); + fireProp0Changed(newValue); + } + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onProp3Changed(Enum3 newValue) + { + Log.i(TAG, "onProp3Changed, will pass notification to all listeners"); + fireProp3Changed(newValue); + } + public void onSig0(Enum0 param0) + { + Log.i(TAG, "onSig0, will pass notification to all listeners"); + fireSig0(param0); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Enum2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param2); + } + public void onSig3(Enum3 param3) + { + Log.i(TAG, "onSig3, will pass notification to all listeners"); + fireSig3(param3); + } + +} diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java new file mode 100644 index 0000000..9f74071 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbEnum.tbEnumjniservice; + +import tbEnum.tbEnum_android_service.IEnumInterfaceServiceFactory; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_api.AbstractEnumInterface; +import tbEnum.tbEnumjniservice.EnumInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton EnumInterfaceJniServiceFactory thread for the system. This is a thread for + * EnumInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class EnumInterfaceJniServiceFactory extends HandlerThread implements IEnumInterfaceServiceFactory +{ + private EnumInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static EnumInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: EnumInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractEnumInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new EnumInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new EnumInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final EnumInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private EnumInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static EnumInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + EnumInterfaceJniServiceFactory t = new EnumInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java new file mode 100644 index 0000000..03d2096 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbEnum.tbEnumjniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter; +import tbEnum.tbEnumjniservice.EnumInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class EnumInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "EnumInterfaceJniStarter"; + + + + public static IEnumInterface start(Context context) { + stop(context); + androidService = new Intent(context, EnumInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + EnumInterfaceJniServiceFactory factory = EnumInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for EnumInterfaceJniServiceFactory"); + return EnumInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle b/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle new file mode 100644 index 0000000..961cae6 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbEnum.tbEnumserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbEnum.tbEnumserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbEnum_api') + implementation project(':tbEnum_android_messenger') + implementation project(':tbEnum_android_service') + implementation project(':tbEnum_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f5b87fe --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java new file mode 100644 index 0000000..b7e733d --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java @@ -0,0 +1,312 @@ +package tbEnum.tbEnumserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbEnum.tbEnum_android_service.EnumInterfaceServiceAdapter; +import tbEnum.tbEnum_android_service.EnumInterfaceServiceFactory; +import tbEnum.tbEnum_android_service.EnumInterfaceServiceStarter; + +//import message type and parcelabe types +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; +import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_impl.EnumInterfaceService; +import java.util.concurrent.CompletableFuture; + + + +public class TbEnumTestServiceApp extends Activity implements IEnumInterfaceEventListener +{ + + private static final String TAG = "TbEnumTestServiceApp"; + static Intent stub_service = null; + + + private IEnumInterface mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp0 = new Button(this); + bProp0.setText("Set prop0"); + bProp0.setBackgroundColor(Color.GREEN); + + bProp0.setOnClickListener(v -> { + Enum0 newProp0 = mBackend.getProp0(); + //TODO increment + Log.i(TAG, "SET prop0" + newProp0); + mBackend.setProp0(newProp0); + }); + propertyButtonsLine.addView(bProp0); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + Enum1 newProp1 = mBackend.getProp1(); + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mBackend.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + Button bProp2 = new Button(this); + bProp2.setText("Set prop2"); + bProp2.setBackgroundColor(Color.GREEN); + + bProp2.setOnClickListener(v -> { + Enum2 newProp2 = mBackend.getProp2(); + //TODO increment + Log.i(TAG, "SET prop2" + newProp2); + mBackend.setProp2(newProp2); + }); + propertyButtonsLine.addView(bProp2); + Button bProp3 = new Button(this); + bProp3.setText("Set prop3"); + bProp3.setBackgroundColor(Color.GREEN); + + bProp3.setOnClickListener(v -> { + Enum3 newProp3 = mBackend.getProp3(); + //TODO increment + Log.i(TAG, "SET prop3" + newProp3); + mBackend.setProp3(newProp3); + }); + propertyButtonsLine.addView(bProp3); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSig0 = new Button(this); + bSig0.setText("sig0"); + + bSig0.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig0 "); + Enum0 param0 = Enum0.Value1; + mBackend.fireSig0(param0); + }); + bSig0.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig0); + Button bSig1 = new Button(this); + bSig1.setText("sig1"); + + bSig1.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig1 "); + Enum1 param1 = Enum1.Value2; + mBackend.fireSig1(param1); + }); + bSig1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig1); + Button bSig2 = new Button(this); + bSig2.setText("sig2"); + + bSig2.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig2 "); + Enum2 param2 = Enum2.Value1; + mBackend.fireSig2(param2); + }); + bSig2.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig2); + Button bSig3 = new Button(this); + bSig3.setText("sig3"); + + bSig3.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig3 "); + Enum3 param3 = Enum3.Value2; + mBackend.fireSig3(param3); + }); + bSig3.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig3); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, EnumInterfaceServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = EnumInterfaceServiceAdapter.setService(EnumInterfaceServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onProp0Changed(Enum0 newValue) + { + outputTextViewProp.setText("Property from service: prop0 " + newValue); + Log.w(TAG, "Property from service: prop0 " + newValue); + } + @Override + public void onProp1Changed(Enum1 newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + outputTextViewProp.setText("Property from service: prop2 " + newValue); + Log.w(TAG, "Property from service: prop2 " + newValue); + } + @Override + public void onProp3Changed(Enum3 newValue) + { + outputTextViewProp.setText("Property from service: prop3 " + newValue); + Log.w(TAG, "Property from service: prop3 " + newValue); + } + @Override + public void onSig0(Enum0 param0) + { + String text = "Signal sig0 "+ " " + param0; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig1(Enum1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig2(Enum2 param2) + { + String text = "Signal sig2 "+ " " + param2; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig3(Enum3 param3) + { + String text = "Signal sig3 "+ " " + param3; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 zcmV-Y3$650Nk&FW3jhFDMM6+kP&il$0000G0000-002h-06|PpNWB9900E$G+qN-D z+81ABX7q?;bwx%xBg?kcwr$(C-Tex-ZCkHUw(Y9#+`E5-zuONG5fgw~E2WDng@Bc@ z24xy+R1n%~6xI#u9vJ8zREI)sb<&Il(016}Z~V1n^PU3-_H17A*Bf^o)&{_uBv}Py zulRfeE8g(g6HFhk_?o_;0@tz?1I+l+Y#Q*;RVC?(ud`_cU-~n|AX-b`JHrOIqn(-t&rOg-o`#C zh0LPxmbOAEb;zHTu!R3LDh1QO zZTf-|lJNUxi-PpcbRjw3n~n-pG;$+dIF6eqM5+L();B2O2tQ~|p{PlpNcvDbd1l%c zLtXn%lu(3!aNK!V#+HNn_D3lp z2%l+hK-nsj|Bi9;V*WIcQRTt5j90A<=am+cc`J zTYIN|PsYAhJ|=&h*4wI4ebv-C=Be#u>}%m;a{IGmJDU`0snWS&$9zdrT(z8#{OZ_Y zxwJx!ZClUi%YJjD6Xz@OP8{ieyJB=tn?>zaI-4JN;rr`JQbb%y5h2O-?_V@7pG_+y z(lqAsqYr!NyVb0C^|uclHaeecG)Sz;WV?rtoqOdAAN{j%?Uo%owya(F&qps@Id|Of zo@~Y-(YmfB+chv^%*3g4k3R0WqvuYUIA+8^SGJ{2Bl$X&X&v02>+0$4?di(34{pt* zG=f#yMs@Y|b&=HyH3k4yP&goF2LJ#tBLJNNDo6lG06r}ghC-pC4Q*=x3;|+W04zte zAl>l4kzUBQFYF(E`KJy?ZXd1tnfbH+Z~SMmA21KokJNs#eqcXWKUIC>{TuoKe^vhF z);H)o`t9j~`$h1D`#bxe@E`oE`cM9w(@)5Bp8BNukIwM>wZHfd0S;5bcXA*5KT3bj zc&_~`&{z7u{Et!Z_k78H75gXf4g8<_ul!H$eVspPeU3j&&Au=2R*Zp#M9$9s;fqwgzfiX=E_?BwVcfx3tG9Q-+<5fw z%Hs64z)@Q*%s3_Xd5>S4dg$s>@rN^ixeVj*tqu3ZV)biDcFf&l?lGwsa zWj3rvK}?43c{IruV2L`hUU0t^MemAn3U~x3$4mFDxj=Byowu^Q+#wKRPrWywLjIAp z9*n}eQ9-gZmnd9Y0WHtwi2sn6n~?i#n9VN1B*074_VbZZ=WrpkMYr{RsI ztM_8X1)J*DZejxkjOTRJ&a*lrvMKBQURNP#K)a5wIitfu(CFYV4FT?LUB$jVwJSZz zNBFTWg->Yk0j&h3e*a5>B=-xM7dE`IuOQna!u$OoxLlE;WdrNlN)1 z7**de7-hZ!(%_ZllHBLg`Ir#|t>2$*xVOZ-ADZKTN?{(NUeLU9GbuG-+Axf*AZ-P1 z0ZZ*fx+ck4{XtFsbcc%GRStht@q!m*ImssGwuK+P@%gEK!f5dHymg<9nSCXsB6 zQ*{<`%^bxB($Z@5286^-A(tR;r+p7B%^%$N5h%lb*Vlz-?DL9x;!j<5>~kmXP$E}m zQV|7uv4SwFs0jUervsxVUm>&9Y3DBIzc1XW|CUZrUdb<&{@D5yuLe%Xniw^x&{A2s z0q1+owDSfc3Gs?ht;3jw49c#mmrViUfX-yvc_B*wY|Lo7; zGh!t2R#BHx{1wFXReX*~`NS-LpSX z#TV*miO^~B9PF%O0huw!1Zv>^d0G3$^8dsC6VI!$oKDKiXdJt{mGkyA`+Gwd4D-^1qtNTUK)`N*=NTG-6}=5k6suNfdLt*dt8D| z%H#$k)z#ZRcf|zDWB|pn<3+7Nz>?WW9WdkO5(a^m+D4WRJ9{wc>Y}IN)2Kbgn;_O? zGqdr&9~|$Y0tP=N(k7^Eu;iO*w+f%W`20BNo)=Xa@M_)+o$4LXJyiw{F?a633SC{B zl~9FH%?^Rm*LVz`lkULs)%idDX^O)SxQol(3jDRyBVR!7d`;ar+D7do)jQ}m`g$TevUD5@?*P8)voa?kEe@_hl{_h8j&5eB-5FrYW&*FHVt$ z$kRF9Nstj%KRzpjdd_9wO=4zO8ritN*NPk_9avYrsF(!4))tm{Ga#OY z(r{0buexOzu7+rw8E08Gxd`LTOID{*AC1m*6Nw@osfB%0oBF5sf<~wH1kL;sd zo)k6^VyRFU`)dt*iX^9&QtWbo6yE8XXH?`ztvpiOLgI3R+=MOBQ9=rMVgi<*CU%+d1PQQ0a1U=&b0vkF207%xU0ssI2 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/colors.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/strings.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..f0703ea --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbEnumTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/themes.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/gradle.properties b/goldenmaster/tbNames/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbNames/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbNames/gradle/libs.versions.toml b/goldenmaster/tbNames/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbNames/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbNames/settings.gradle b/goldenmaster/tbNames/settings.gradle new file mode 100644 index 0000000..334c9c5 --- /dev/null +++ b/goldenmaster/tbNames/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbNames" +include ':tbNames_android_service' +include ':tbNames_android_client' +include ':tbNames_android_messenger' +include ':tbNames_impl' +include ':tbNames_api' +include ':tbNames_client_example' +include ':tbNamesserviceexample' \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_android_client/additions.gradle b/goldenmaster/tbNames/tbNames_android_client/additions.gradle new file mode 100644 index 0000000..399e6cd --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'tbNames.tbNames_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + implementation project(':tbNames_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_android_client/build.gradle b/goldenmaster/tbNames/tbNames_android_client/build.gradle new file mode 100644 index 0000000..49e70e2 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbNames" +version = "1.0.0" + +android { + namespace 'tbNames.tbNames_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + implementation project(':tbNames_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java new file mode 100644 index 0000000..0c14ffb --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java @@ -0,0 +1,497 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbNames.tbNames_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_android_messenger.NamEsMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NamEsClient extends AbstractNamEs implements ServiceConnection +{ + private static final String TAG = "NamEsClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private boolean m_Switch = false; + private int m_SOME_PROPERTY = 0; + private int m_Some_Poperty2 = 0; + + + public NamEsClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NamEsServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbNames.tbNames_android_service.NamEsServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NamEsMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NamEsMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NamEsMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + boolean Switch = data.getBoolean("Switch", false); + onSwitch(Switch); + + int SOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); + onSomeProperty(SOME_PROPERTY); + + int Some_Poperty2 = data.getInt("Some_Poperty2", 0); + onSomePoperty2(Some_Poperty2); + + break; + } + case SET_Switch: + { + Bundle data = msg.getData(); + + + boolean Switch = data.getBoolean("Switch", false); + + onSwitch(Switch); + break; + } + case SET_SomeProperty: + { + Bundle data = msg.getData(); + + + int SOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); + + onSomeProperty(SOME_PROPERTY); + break; + } + case SET_SomePoperty2: + { + Bundle data = msg.getData(); + + + int Some_Poperty2 = data.getInt("Some_Poperty2", 0); + + onSomePoperty2(Some_Poperty2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SomeSignal: { + + Bundle data = msg.getData(); + + boolean SOME_PARAM = data.getBoolean("SOME_PARAM", false); + onSomeSignal(SOME_PARAM); + break; + } + case SIG_SomeSignal2: { + + Bundle data = msg.getData(); + + boolean Some_Param = data.getBoolean("Some_Param", false); + onSomeSignal2(Some_Param); + break; + } + case RPC_SomeFunctionResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NamEsMessageType.RPC_SomeFunctionResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_SomeFunction2Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NamEsMessageType.RPC_SomeFunction2Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setSwitch(boolean Switch) + { + Log.i(TAG, "request setSwitch called "+ Switch); + if (m_Switch != Switch) + { + Message msg = new Message(); + msg.what = NamEsMessageType.PROP_Switch.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("Switch", Switch); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onSwitch(boolean Switch) + { + Log.i(TAG, "value received from service for Switch "); + if (m_Switch != Switch) + { + m_Switch = Switch; + fireSwitchChanged(Switch); + } + + } + + @Override + public boolean getSwitch() + { + Log.i(TAG, "request getSwitch called, returning local"); + return m_Switch; + } + + + @Override + public void setSomeProperty(int SOME_PROPERTY) + { + Log.i(TAG, "request setSomeProperty called "+ SOME_PROPERTY); + if (m_SOME_PROPERTY != SOME_PROPERTY) + { + Message msg = new Message(); + msg.what = NamEsMessageType.PROP_SomeProperty.getValue(); + Bundle data = new Bundle(); + + data.putInt("SOME_PROPERTY", SOME_PROPERTY); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onSomeProperty(int SOME_PROPERTY) + { + Log.i(TAG, "value received from service for SomeProperty "); + if (m_SOME_PROPERTY != SOME_PROPERTY) + { + m_SOME_PROPERTY = SOME_PROPERTY; + fireSomePropertyChanged(SOME_PROPERTY); + } + + } + + @Override + public int getSomeProperty() + { + Log.i(TAG, "request getSomeProperty called, returning local"); + return m_SOME_PROPERTY; + } + + + @Override + public void setSomePoperty2(int Some_Poperty2) + { + Log.i(TAG, "request setSomePoperty2 called "+ Some_Poperty2); + if (m_Some_Poperty2 != Some_Poperty2) + { + Message msg = new Message(); + msg.what = NamEsMessageType.PROP_SomePoperty2.getValue(); + Bundle data = new Bundle(); + + data.putInt("Some_Poperty2", Some_Poperty2); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onSomePoperty2(int Some_Poperty2) + { + Log.i(TAG, "value received from service for SomePoperty2 "); + if (m_Some_Poperty2 != Some_Poperty2) + { + m_Some_Poperty2 = Some_Poperty2; + fireSomePoperty2Changed(Some_Poperty2); + } + + } + + @Override + public int getSomePoperty2() + { + Log.i(TAG, "request getSomePoperty2 called, returning local"); + return m_Some_Poperty2; + } + + + // methods + + + @Override + public void someFunction(boolean SOME_PARAM) { + CompletableFuture resFuture = someFunctionAsync(SOME_PARAM); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture someFunctionAsync(boolean SOME_PARAM) { + + Log.i(TAG, "Call on service someFunction "+ " " + SOME_PARAM); + Message msg = new Message(); + msg.what = NamEsMessageType.RPC_SomeFunctionReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBoolean("SOME_PARAM", SOME_PARAM); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve SOME_FUNCTION"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public void someFunction2(boolean Some_Param) { + CompletableFuture resFuture = someFunction2Async(Some_Param); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture someFunction2Async(boolean Some_Param) { + + Log.i(TAG, "Call on service someFunction2 "+ " " + Some_Param); + Message msg = new Message(); + msg.what = NamEsMessageType.RPC_SomeFunction2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBoolean("Some_Param", Some_Param); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve Some_Function2"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSomeSignal(boolean SOME_PARAM) + { + Log.i(TAG, "onSomeSignal received from service"); + fireSomeSignal(SOME_PARAM); + } + public void onSomeSignal2(boolean Some_Param) + { + Log.i(TAG, "onSomeSignal2 received from service"); + fireSomeSignal2(Some_Param); + } +} diff --git a/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java new file mode 100644 index 0000000..f4d416f --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java @@ -0,0 +1,365 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbNames.tbNames_android_client; + +import tbNames.tbNames_android_client.NamEsClient; + +//import message type and parcelabe types +import tbNames.tbNames_api.TbNamesTestHelper; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_android_messenger.NamEsMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INamEsClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NamEsClientTest +{ + + @Mock + private Context mMockContext; + + private NamEsClient testedClient; + private INamEsEventListener listenerMock = mock(INamEsEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INamEsClientMessageGetter serviceMessagesStorage = mock(INamEsClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NamEsMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INamEsClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NamEsClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbNames.tbNames_android_service", "tbNames.tbNames_android_service.NamEsServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NamEsMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NamEsMessageType.INIT.getValue()); + Bundle data = new Bundle(); + boolean testSwitch = true; + data.putBoolean("Switch", testSwitch); + int testSOME_PROPERTY = 1; + data.putInt("SOME_PROPERTY", testSOME_PROPERTY); + int testSome_Poperty2 = 1; + data.putInt("Some_Poperty2", testSome_Poperty2); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onSwitchChanged(testSwitch); + inOrderEventListener.verify(listenerMock,times(1)).onSomePropertyChanged(testSOME_PROPERTY); + inOrderEventListener.verify(listenerMock,times(1)).onSomePoperty2Changed(testSome_Poperty2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveSwitchPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.SET_Switch.getValue()); + Bundle data = new Bundle(); + boolean testSwitch = true; + data.putBoolean("Switch", testSwitch); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onSwitchChanged(testSwitch); + } + + @Test + public void setPropertyRequestSwitch() + { + boolean testSwitch = true; + + testedClient.setSwitch(testSwitch); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.PROP_Switch.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedSwitch = data.getBoolean("Switch", false); + assertEquals(receivedSwitch, testSwitch); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveSOME_PROPERTYPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.SET_SomeProperty.getValue()); + Bundle data = new Bundle(); + int testSOME_PROPERTY = 1; + data.putInt("SOME_PROPERTY", testSOME_PROPERTY); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onSomePropertyChanged(testSOME_PROPERTY); + } + + @Test + public void setPropertyRequestSOME_PROPERTY() + { + int testSOME_PROPERTY = 1; + + testedClient.setSomeProperty(testSOME_PROPERTY); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.PROP_SomeProperty.getValue(), response.what); + Bundle data = response.getData(); + + int receivedSOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); + assertEquals(receivedSOME_PROPERTY, testSOME_PROPERTY); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveSome_Poperty2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.SET_SomePoperty2.getValue()); + Bundle data = new Bundle(); + int testSome_Poperty2 = 1; + data.putInt("Some_Poperty2", testSome_Poperty2); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onSomePoperty2Changed(testSome_Poperty2); + } + + @Test + public void setPropertyRequestSome_Poperty2() + { + int testSome_Poperty2 = 1; + + testedClient.setSomePoperty2(testSome_Poperty2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.PROP_SomePoperty2.getValue(), response.what); + Bundle data = response.getData(); + + int receivedSome_Poperty2 = data.getInt("Some_Poperty2", 0); + assertEquals(receivedSome_Poperty2, testSome_Poperty2); + } + @Test + public void whenNotifiedSOME_SIGNAL() throws RemoteException + { + + Message msg = Message.obtain(null, NamEsMessageType.SIG_SomeSignal.getValue()); + Bundle data = new Bundle(); + boolean testSOME_PARAM = true; + data.putBoolean("SOME_PARAM", testSOME_PARAM); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSomeSignal(testSOME_PARAM); + +} + @Test + public void whenNotifiedSome_Signal2() throws RemoteException + { + + Message msg = Message.obtain(null, NamEsMessageType.SIG_SomeSignal2.getValue()); + Bundle data = new Bundle(); + boolean testSome_Param = true; + data.putBoolean("Some_Param", testSome_Param); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSomeSignal2(testSome_Param); + +} + + + public void onSOME_FUNCTIONRequest() throws RemoteException { + + // Execute method + boolean testSOME_PARAM = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.someFunctionAsync(testSOME_PARAM); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NamEsMessageType.RPC_SomeFunctionReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean receivedSOME_PARAM = data.getBoolean("SOME_PARAM", false); + assertEquals(receivedSOME_PARAM, testSOME_PARAM); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NamEsMessageType.RPC_SomeFunctionResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onSome_Function2Request() throws RemoteException { + + // Execute method + boolean testSome_Param = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.someFunction2Async(testSome_Param); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NamEsMessageType.RPC_SomeFunction2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean receivedSome_Param = data.getBoolean("Some_Param", false); + assertEquals(receivedSome_Param, testSome_Param); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NamEsMessageType.RPC_SomeFunction2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbNames/tbNames_android_messenger/additions.gradle b/goldenmaster/tbNames/tbNames_android_messenger/additions.gradle new file mode 100644 index 0000000..822fef6 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbNames.tbNames_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_android_messenger/build.gradle b/goldenmaster/tbNames/tbNames_android_messenger/build.gradle new file mode 100644 index 0000000..735f9dd --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbNames" +version = "1.0.0" + +android { + namespace 'tbNames.tbNames_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java new file mode 100644 index 0000000..70f5d8e --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java @@ -0,0 +1,42 @@ +package tbNames.tbNames_android_messenger; + +public enum NamEsMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Switch(3), + SET_Switch(4), + PROP_SomeProperty(5), + SET_SomeProperty(6), + PROP_SomePoperty2(7), + SET_SomePoperty2(8), + SIG_SomeSignal(9), + SIG_SomeSignal2(10), + RPC_SomeFunctionReq(11), + RPC_SomeFunctionResp(12), + RPC_SomeFunction2Req(13), + RPC_SomeFunction2Resp(14), + NamEsMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NamEsMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NamEsMessageType fromInteger(int value) + { + for (NamEsMessageType event : NamEsMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NamEsMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbNames/tbNames_android_service/additions.gradle b/goldenmaster/tbNames/tbNames_android_service/additions.gradle new file mode 100644 index 0000000..efbea79 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbNames.tbNames_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + implementation project(':tbNames_impl') + implementation project(':tbNames_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_android_service/build.gradle b/goldenmaster/tbNames/tbNames_android_service/build.gradle new file mode 100644 index 0000000..30ac99d --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbNames" +version = "1.0.0" + + +android { + namespace 'tbNames.tbNames_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + implementation project(':tbNames_impl') + implementation project(':tbNames_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbNames/tbNames_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9df9865 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/INamEsServiceFactory.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/INamEsServiceFactory.java new file mode 100644 index 0000000..0f25cf9 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/INamEsServiceFactory.java @@ -0,0 +1,7 @@ +package tbNames.tbNames_android_service; +import tbNames.tbNames_api.INamEs; + + +public interface INamEsServiceFactory { + public INamEs getServiceInstance(); +} diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java new file mode 100644 index 0000000..f7c30e5 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java @@ -0,0 +1,371 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbNames.tbNames_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_android_service.INamEsServiceFactory; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_android_messenger.NamEsMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NamEsServiceAdapter extends Service +{ + private static final String TAG = "NamEsServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INamEs mBackendService; + private static INamEsServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NamEsServiceAdapter() + { + } + + public static INamEs setService(INamEsServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NamEsService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NamEsService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NamEsService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NamEsService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INamEsEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NamEsMessageType.fromInteger(msg.what) != NamEsMessageType.REGISTER_CLIENT + && NamEsMessageType.fromInteger(msg.what) != NamEsMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NamEsMessageType" + NamEsMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NamEsMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Switch: + { + Bundle data = msg.getData(); + + boolean Switch = data.getBoolean("Switch", false); + mBackendService.setSwitch(Switch); + break; + } + case PROP_SomeProperty: + { + Bundle data = msg.getData(); + + int SOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); + mBackendService.setSomeProperty(SOME_PROPERTY); + break; + } + case PROP_SomePoperty2: + { + Bundle data = msg.getData(); + + int Some_Poperty2 = data.getInt("Some_Poperty2", 0); + mBackendService.setSomePoperty2(Some_Poperty2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_SomeFunctionReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean SOME_PARAM = data.getBoolean("SOME_PARAM", false); + + mBackendService.someFunction(SOME_PARAM); + + Message respMsg = new Message(); + respMsg.what = NamEsMessageType.RPC_SomeFunctionResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_SomeFunction2Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean Some_Param = data.getBoolean("Some_Param", false); + + mBackendService.someFunction2(Some_Param); + + Message respMsg = new Message(); + respMsg.what = NamEsMessageType.RPC_SomeFunction2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NamEsMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + boolean Switch = mBackendService.getSwitch(); + + data.putBoolean("Switch", Switch); + int SOME_PROPERTY = mBackendService.getSomeProperty(); + + data.putInt("SOME_PROPERTY", SOME_PROPERTY); + int Some_Poperty2 = mBackendService.getSomePoperty2(); + + data.putInt("Some_Poperty2", Some_Poperty2); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSwitchChanged(boolean Switch){ + Log.i(TAG, "New value for Switch from backend" + Switch); + + Message msg = new Message(); + msg.what = NamEsMessageType.SET_Switch.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("Switch", Switch); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSomePropertyChanged(int SOME_PROPERTY){ + Log.i(TAG, "New value for SomeProperty from backend" + SOME_PROPERTY); + + Message msg = new Message(); + msg.what = NamEsMessageType.SET_SomeProperty.getValue(); + Bundle data = new Bundle(); + + data.putInt("SOME_PROPERTY", SOME_PROPERTY); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSomePoperty2Changed(int Some_Poperty2){ + Log.i(TAG, "New value for SomePoperty2 from backend" + Some_Poperty2); + + Message msg = new Message(); + msg.what = NamEsMessageType.SET_SomePoperty2.getValue(); + Bundle data = new Bundle(); + + data.putInt("Some_Poperty2", Some_Poperty2); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSomeSignal(boolean SOME_PARAM){ + Log.i(TAG, "New singal for SomeSignal = "+ " " + SOME_PARAM); + Message msg = new Message(); + msg.what = NamEsMessageType.SIG_SomeSignal.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("SOME_PARAM", SOME_PARAM); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSomeSignal2(boolean Some_Param){ + Log.i(TAG, "New singal for SomeSignal2 = "+ " " + Some_Param); + Message msg = new Message(); + msg.what = NamEsMessageType.SIG_SomeSignal2.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("Some_Param", Some_Param); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java new file mode 100644 index 0000000..4109426 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbNames.tbNames_android_service; + +import tbNames.tbNames_android_service.INamEsServiceFactory; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_impl.NamEsService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NamEsServiceFactory thread for the system. This is a thread for + * NamEsServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NamEsServiceFactory extends HandlerThread implements INamEsServiceFactory +{ + private NamEsService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NamEsServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NamEsServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNamEs getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NamEsService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NamEsService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NamEsServiceFactory INSTANCE = createInstance(); + } + + private NamEsServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NamEsServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NamEsServiceFactory t = new NamEsServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java new file mode 100644 index 0000000..3c7d29e --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java @@ -0,0 +1,42 @@ +package tbNames.tbNames_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_android_service.NamEsServiceAdapter; +import tbNames.tbNames_android_service.NamEsServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbNames.tbNames_impl; . +public class NamEsServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NamEsStarter"; + + + + public static INamEs start(Context context) { + stop(context); + androidService = new Intent(context, NamEsServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NamEsServiceFactory factory = NamEsServiceFactory.get(); + Log.w(TAG, "starter: factory set for NamEsServiceFactory"); + return NamEsServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java new file mode 100644 index 0000000..cbba108 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java @@ -0,0 +1,402 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbNames.tbNames_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbNames.tbNames_android_service.NamEsServiceAdapter; + +//import message type and parcelabe types +import tbNames.tbNames_api.TbNamesTestHelper; + + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_android_service.INamEsServiceFactory; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_android_messenger.NamEsMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INamEsMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NamEsServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NamEsServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INamEsEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INamEs backendServiceMock = mock(INamEs.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INamEsServiceFactory serviceFactory = mock(INamEsServiceFactory.class); + private INamEsMessageGetter clientMessagesStorage = mock(INamEsMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NamEsMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INamEsMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + boolean initSwitch = true; + when(backendServiceMock.getSwitch()).thenReturn(initSwitch); + int initSOME_PROPERTY = 1; + when(backendServiceMock.getSomeProperty()).thenReturn(initSOME_PROPERTY); + int initSome_Poperty2 = 1; + when(backendServiceMock.getSomePoperty2()).thenReturn(initSome_Poperty2); + + + Message registerMsg = Message.obtain(null, NamEsMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getSwitch(); + inOrderBackendService.verify(backendServiceMock, times(1)).getSomeProperty(); + inOrderBackendService.verify(backendServiceMock, times(1)).getSomePoperty2(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedSwitch = data.getBoolean("Switch", false); + + int receivedSOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); + + int receivedSome_Poperty2 = data.getInt("Some_Poperty2", 0); + assertEquals(receivedSwitch, initSwitch); + assertEquals(receivedSOME_PROPERTY, initSOME_PROPERTY); + assertEquals(receivedSome_Poperty2, initSome_Poperty2); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NamEsServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NamEsServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INamEsEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveSwitchPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.PROP_Switch.getValue()); + Bundle data = new Bundle(); + boolean testSwitch = true; + data.putBoolean("Switch", testSwitch); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setSwitch(testSwitch); + + } + + @Test + public void whenNotifiedSwitch() + { + boolean testSwitch = true; + + testedAdapterAsEventListener.onSwitchChanged(testSwitch); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.SET_Switch.getValue(), response.what); + Bundle data = response.getData(); + + + boolean receivedSwitch = data.getBoolean("Switch", false); + + assertEquals(receivedSwitch, testSwitch); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveSOME_PROPERTYPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.PROP_SomeProperty.getValue()); + Bundle data = new Bundle(); + int testSOME_PROPERTY = 1; + data.putInt("SOME_PROPERTY", testSOME_PROPERTY); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setSomeProperty(testSOME_PROPERTY); + + } + + @Test + public void whenNotifiedSOME_PROPERTY() + { + int testSOME_PROPERTY = 1; + + testedAdapterAsEventListener.onSomePropertyChanged(testSOME_PROPERTY); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.SET_SomeProperty.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedSOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); + + assertEquals(receivedSOME_PROPERTY, testSOME_PROPERTY); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveSome_Poperty2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.PROP_SomePoperty2.getValue()); + Bundle data = new Bundle(); + int testSome_Poperty2 = 1; + data.putInt("Some_Poperty2", testSome_Poperty2); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setSomePoperty2(testSome_Poperty2); + + } + + @Test + public void whenNotifiedSome_Poperty2() + { + int testSome_Poperty2 = 1; + + testedAdapterAsEventListener.onSomePoperty2Changed(testSome_Poperty2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.SET_SomePoperty2.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedSome_Poperty2 = data.getInt("Some_Poperty2", 0); + + assertEquals(receivedSome_Poperty2, testSome_Poperty2); + } + @Test + public void whenNotifiedSOME_SIGNAL() + { + boolean testSOME_PARAM = true; + + testedAdapterAsEventListener.onSomeSignal(testSOME_PARAM); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.SIG_SomeSignal.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedSOME_PARAM = data.getBoolean("SOME_PARAM", false); + assertEquals(receivedSOME_PARAM, testSOME_PARAM); +} + @Test + public void whenNotifiedSome_Signal2() + { + boolean testSome_Param = true; + + testedAdapterAsEventListener.onSomeSignal2(testSome_Param); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.SIG_SomeSignal2.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedSome_Param = data.getBoolean("Some_Param", false); + assertEquals(receivedSome_Param, testSome_Param); +} + + + public void onSOME_FUNCTIONRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.RPC_SomeFunctionReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean testSOME_PARAM = true; + data.putBoolean("SOME_PARAM", testSOME_PARAM); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).someFunction(testSOME_PARAM); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.RPC_SomeFunctionResp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onSome_Function2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.RPC_SomeFunction2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean testSome_Param = true; + data.putBoolean("Some_Param", testSome_Param); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).someFunction2(testSome_Param); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.RPC_SomeFunction2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbNames/tbNames_api/additions.gradle b/goldenmaster/tbNames/tbNames_api/additions.gradle new file mode 100644 index 0000000..186267c --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'tbNames.tbNames_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/tbNames/tbNames_api/build.gradle b/goldenmaster/tbNames/tbNames_api/build.gradle new file mode 100644 index 0000000..9158afe --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "tbNames" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java new file mode 100644 index 0000000..49de190 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java @@ -0,0 +1,60 @@ +package tbNames.tbNames_api; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNamEs implements INamEs { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INamEsEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INamEsEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireSwitchChanged(boolean newValue) { + for (INamEsEventListener listener : listeners) { + listener.onSwitchChanged(newValue); + } + } + + @Override + public void fireSomePropertyChanged(int newValue) { + for (INamEsEventListener listener : listeners) { + listener.onSomePropertyChanged(newValue); + } + } + + @Override + public void fireSomePoperty2Changed(int newValue) { + for (INamEsEventListener listener : listeners) { + listener.onSomePoperty2Changed(newValue); + } + } + + @Override + public void fireSomeSignal(boolean SOME_PARAM) { + for (INamEsEventListener listener : listeners) { + listener.onSomeSignal(SOME_PARAM); + } + } + + @Override + public void fireSomeSignal2(boolean Some_Param) { + for (INamEsEventListener listener : listeners) { + listener.onSomeSignal2(Some_Param); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INamEsEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java new file mode 100644 index 0000000..1431d0a --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java @@ -0,0 +1,34 @@ +package tbNames.tbNames_api; + +import tbNames.tbNames_api.INamEsEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface INamEs { + // properties + void setSwitch(boolean Switch); + boolean getSwitch(); + void fireSwitchChanged(boolean newValue); + + void setSomeProperty(int SOME_PROPERTY); + int getSomeProperty(); + void fireSomePropertyChanged(int newValue); + + void setSomePoperty2(int Some_Poperty2); + int getSomePoperty2(); + void fireSomePoperty2Changed(int newValue); + + // methods + void someFunction(boolean SOME_PARAM); + CompletableFuture someFunctionAsync(boolean SOME_PARAM); + void someFunction2(boolean Some_Param); + CompletableFuture someFunction2Async(boolean Some_Param); + public void fireSomeSignal(boolean SOME_PARAM); + public void fireSomeSignal2(boolean Some_Param); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INamEsEventListener listener); + void removeEventListener(INamEsEventListener listener); + } diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java new file mode 100644 index 0000000..fdd1058 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java @@ -0,0 +1,10 @@ +package tbNames.tbNames_api; + + public interface INamEsEventListener { + void onSwitchChanged(boolean newValue); + void onSomePropertyChanged(int newValue); + void onSomePoperty2Changed(int newValue); + void onSomeSignal(boolean SOME_PARAM); + void onSomeSignal2(boolean Some_Param); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java new file mode 100644 index 0000000..30e05c1 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java @@ -0,0 +1,11 @@ +package tbNames.tbNames_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class TbNamesTestHelper +{ + +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/build.gradle b/goldenmaster/tbNames/tbNames_client_example/build.gradle new file mode 100644 index 0000000..f4ecb7f --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbNames.tbNames_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbNames.tbNames_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbNames_api') + implementation project(':tbNames_android_messenger') + implementation project(':tbNames_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..20f352c --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java new file mode 100644 index 0000000..90c62b7 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java @@ -0,0 +1,288 @@ +package tbNames.tbNames_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbNames.tbNames_android_client.NamEsClient; + +//import message type and parcelabe types + +import tbNames.tbNames_api.INamEsEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbNamesTestClientApp extends Activity implements INamEsEventListener +{ + + private static final String TAG = "TbNamesTestClientApp"; + + private NamEsClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbNames.tbNamesserviceexample.TbNamesTestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSwitch = new Button(this); + bSwitch.setText("Set Switch"); + bSwitch.setBackgroundColor(Color.GREEN); + + bSwitch.setOnClickListener(v -> { + boolean newSwitch = mClient.getSwitch(); + + //TODO increment + Log.i(TAG, "SET Switch" + newSwitch); + mClient.setSwitch(newSwitch); + }); + propertyButtonsLine.addView(bSwitch); + Button bSomeProperty = new Button(this); + bSomeProperty.setText("Set SOME_PROPERTY"); + bSomeProperty.setBackgroundColor(Color.GREEN); + + bSomeProperty.setOnClickListener(v -> { + int newSomeProperty = mClient.getSomeProperty(); + + //TODO increment + Log.i(TAG, "SET SOME_PROPERTY" + newSomeProperty); + mClient.setSomeProperty(newSomeProperty); + }); + propertyButtonsLine.addView(bSomeProperty); + Button bSomePoperty2 = new Button(this); + bSomePoperty2.setText("Set Some_Poperty2"); + bSomePoperty2.setBackgroundColor(Color.GREEN); + + bSomePoperty2.setOnClickListener(v -> { + int newSomePoperty2 = mClient.getSomePoperty2(); + + //TODO increment + Log.i(TAG, "SET Some_Poperty2" + newSomePoperty2); + mClient.setSomePoperty2(newSomePoperty2); + }); + propertyButtonsLine.addView(bSomePoperty2); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSomeFunction = new Button(this); + bSomeFunction.setText("SOME_FUNCTION"); + + bSomeFunction.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD SOME_FUNCTION "); + boolean SOME_PARAM = true; + CompletableFuture method_res + = mClient.someFunctionAsync(SOME_PARAM).thenApply( + i -> { + outputTextVieMethodRes.setText("Got SOME_FUNCTION result "+ i); + return i; + }); + }); + bSomeFunction.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSomeFunction); + Button bSomeFunction2 = new Button(this); + bSomeFunction2.setText("Some_Function2"); + + bSomeFunction2.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD Some_Function2 "); + boolean Some_Param = true; + CompletableFuture method_res + = mClient.someFunction2Async(Some_Param).thenApply( + i -> { + outputTextVieMethodRes.setText("Got Some_Function2 result "+ i); + return i; + }); + }); + bSomeFunction2.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSomeFunction2); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new NamEsClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onSwitchChanged(boolean newValue) + { + outputTextViewProp.setText("Property from service: Switch " + newValue); + Log.w(TAG, "Property from service: Switch " + newValue); + } + @Override + public void onSomePropertyChanged(int newValue) + { + outputTextViewProp.setText("Property from service: SOME_PROPERTY " + newValue); + Log.w(TAG, "Property from service: SOME_PROPERTY " + newValue); + } + @Override + public void onSomePoperty2Changed(int newValue) + { + outputTextViewProp.setText("Property from service: Some_Poperty2 " + newValue); + Log.w(TAG, "Property from service: Some_Poperty2 " + newValue); + } + @Override + public void onSomeSignal(boolean SOME_PARAM) + { + String text = "Signal SOME_SIGNAL "+ " " + SOME_PARAM; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSomeSignal2(boolean Some_Param) + { + String text = "Signal Some_Signal2 "+ " " + Some_Param; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 zcmV-Y3$650Nk&FW3jhFDMM6+kP&il$0000G0000-002h-06|PpNWB9900E$G+qN-D z+81ABX7q?;bwx%xBg?kcwr$(C-Tex-ZCkHUw(Y9#+`E5-zuONG5fgw~E2WDng@Bc@ z24xy+R1n%~6xI#u9vJ8zREI)sb<&Il(016}Z~V1n^PU3-_H17A*Bf^o)&{_uBv}Py zulRfeE8g(g6HFhk_?o_;0@tz?1I+l+Y#Q*;RVC?(ud`_cU-~n|AX-b`JHrOIqn(-t&rOg-o`#C zh0LPxmbOAEb;zHTu!R3LDh1QO zZTf-|lJNUxi-PpcbRjw3n~n-pG;$+dIF6eqM5+L();B2O2tQ~|p{PlpNcvDbd1l%c zLtXn%lu(3!aNK!V#+HNn_D3lp z2%l+hK-nsj|Bi9;V*WIcQRTt5j90A<=am+cc`J zTYIN|PsYAhJ|=&h*4wI4ebv-C=Be#u>}%m;a{IGmJDU`0snWS&$9zdrT(z8#{OZ_Y zxwJx!ZClUi%YJjD6Xz@OP8{ieyJB=tn?>zaI-4JN;rr`JQbb%y5h2O-?_V@7pG_+y z(lqAsqYr!NyVb0C^|uclHaeecG)Sz;WV?rtoqOdAAN{j%?Uo%owya(F&qps@Id|Of zo@~Y-(YmfB+chv^%*3g4k3R0WqvuYUIA+8^SGJ{2Bl$X&X&v02>+0$4?di(34{pt* zG=f#yMs@Y|b&=HyH3k4yP&goF2LJ#tBLJNNDo6lG06r}ghC-pC4Q*=x3;|+W04zte zAl>l4kzUBQFYF(E`KJy?ZXd1tnfbH+Z~SMmA21KokJNs#eqcXWKUIC>{TuoKe^vhF z);H)o`t9j~`$h1D`#bxe@E`oE`cM9w(@)5Bp8BNukIwM>wZHfd0S;5bcXA*5KT3bj zc&_~`&{z7u{Et!Z_k78H75gXf4g8<_ul!H$eVspPeU3j&&Au=2R*Zp#M9$9s;fqwgzfiX=E_?BwVcfx3tG9Q-+<5fw z%Hs64z)@Q*%s3_Xd5>S4dg$s>@rN^ixeVj*tqu3ZV)biDcFf&l?lGwsa zWj3rvK}?43c{IruV2L`hUU0t^MemAn3U~x3$4mFDxj=Byowu^Q+#wKRPrWywLjIAp z9*n}eQ9-gZmnd9Y0WHtwi2sn6n~?i#n9VN1B*074_VbZZ=WrpkMYr{RsI ztM_8X1)J*DZejxkjOTRJ&a*lrvMKBQURNP#K)a5wIitfu(CFYV4FT?LUB$jVwJSZz zNBFTWg->Yk0j&h3e*a5>B=-xM7dE`IuOQna!u$OoxLlE;WdrNlN)1 z7**de7-hZ!(%_ZllHBLg`Ir#|t>2$*xVOZ-ADZKTN?{(NUeLU9GbuG-+Axf*AZ-P1 z0ZZ*fx+ck4{XtFsbcc%GRStht@q!m*ImssGwuK+P@%gEK!f5dHymg<9nSCXsB6 zQ*{<`%^bxB($Z@5286^-A(tR;r+p7B%^%$N5h%lb*Vlz-?DL9x;!j<5>~kmXP$E}m zQV|7uv4SwFs0jUervsxVUm>&9Y3DBIzc1XW|CUZrUdb<&{@D5yuLe%Xniw^x&{A2s z0q1+owDSfc3Gs?ht;3jw49c#mmrViUfX-yvc_B*wY|Lo7; zGh!t2R#BHx{1wFXReX*~`NS-LpSX z#TV*miO^~B9PF%O0huw!1Zv>^d0G3$^8dsC6VI!$oKDKiXdJt{mGkyA`+Gwd4D-^1qtNTUK)`N*=NTG-6}=5k6suNfdLt*dt8D| z%H#$k)z#ZRcf|zDWB|pn<3+7Nz>?WW9WdkO5(a^m+D4WRJ9{wc>Y}IN)2Kbgn;_O? zGqdr&9~|$Y0tP=N(k7^Eu;iO*w+f%W`20BNo)=Xa@M_)+o$4LXJyiw{F?a633SC{B zl~9FH%?^Rm*LVz`lkULs)%idDX^O)SxQol(3jDRyBVR!7d`;ar+D7do)jQ}m`g$TevUD5@?*P8)voa?kEe@_hl{_h8j&5eB-5FrYW&*FHVt$ z$kRF9Nstj%KRzpjdd_9wO=4zO8ritN*NPk_9avYrsF(!4))tm{Ga#OY z(r{0buexOzu7+rw8E08Gxd`LTOID{*AC1m*6Nw@osfB%0oBF5sf<~wH1kL;sd zo)k6^VyRFU`)dt*iX^9&QtWbo6yE8XXH?`ztvpiOLgI3R+=MOBQ9=rMVgi<*CU%+d1PQQ0a1U=&b0vkF207%xU0ssI2 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/colors.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/strings.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..e8f4514 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbNamesTestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/themes.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_impl/additions.gradle b/goldenmaster/tbNames/tbNames_impl/additions.gradle new file mode 100644 index 0000000..89405ea --- /dev/null +++ b/goldenmaster/tbNames/tbNames_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbNames.tbNames_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_impl/build.gradle b/goldenmaster/tbNames/tbNames_impl/build.gradle new file mode 100644 index 0000000..1452a87 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbNames" +version = "1.0.0" + +android { + namespace 'tbNames.tbNames_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbNames_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java new file mode 100644 index 0000000..e34c239 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java @@ -0,0 +1,155 @@ +package tbNames.tbNames_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_api.INamEsEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NamEsService extends AbstractNamEs { + + private final static String TAG = "NamEsService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private boolean m_Switch = false; + private int m_SOME_PROPERTY = 0; + private int m_Some_Poperty2 = 0; + + public NamEsService() + { + fire_readyStatusChanged(true); + } + @Override + public void setSwitch(boolean Switch) + { + Log.i(TAG, "request setSwitch called "); + if (m_Switch != Switch) + { + m_Switch = Switch; + onSwitchChanged(m_Switch); + } + + } + + @Override + public boolean getSwitch() + { + Log.i(TAG, "request getSwitch called,"); + return m_Switch; + } + + + @Override + public void setSomeProperty(int SOME_PROPERTY) + { + Log.i(TAG, "request setSomeProperty called "); + if (m_SOME_PROPERTY != SOME_PROPERTY) + { + m_SOME_PROPERTY = SOME_PROPERTY; + onSomePropertyChanged(m_SOME_PROPERTY); + } + + } + + @Override + public int getSomeProperty() + { + Log.i(TAG, "request getSomeProperty called,"); + return m_SOME_PROPERTY; + } + + + @Override + public void setSomePoperty2(int Some_Poperty2) + { + Log.i(TAG, "request setSomePoperty2 called "); + if (m_Some_Poperty2 != Some_Poperty2) + { + m_Some_Poperty2 = Some_Poperty2; + onSomePoperty2Changed(m_Some_Poperty2); + } + + } + + @Override + public int getSomePoperty2() + { + Log.i(TAG, "request getSomePoperty2 called,"); + return m_Some_Poperty2; + } + + + // methods + + @Override + public void someFunction(boolean SOME_PARAM) { + Log.w(TAG, "request method someFunction called, returnig default"); + return ; + } + + @Override + public CompletableFuture someFunctionAsync(boolean SOME_PARAM) { + return CompletableFuture.runAsync( + () -> { someFunction(SOME_PARAM); }, + executor); + } + + @Override + public void someFunction2(boolean Some_Param) { + Log.w(TAG, "request method someFunction2 called, returnig default"); + return ; + } + + @Override + public CompletableFuture someFunction2Async(boolean Some_Param) { + return CompletableFuture.runAsync( + () -> { someFunction2(Some_Param); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onSwitchChanged(boolean newValue) + { + Log.i(TAG, "onSwitchChanged, will pass notification to all listeners"); + fireSwitchChanged(newValue); + } + private void onSomePropertyChanged(int newValue) + { + Log.i(TAG, "onSomePropertyChanged, will pass notification to all listeners"); + fireSomePropertyChanged(newValue); + } + private void onSomePoperty2Changed(int newValue) + { + Log.i(TAG, "onSomePoperty2Changed, will pass notification to all listeners"); + fireSomePoperty2Changed(newValue); + } + public void onSomeSignal(boolean SOME_PARAM) + { + Log.i(TAG, "onSomeSignal, will pass notification to all listeners"); + fireSomeSignal(SOME_PARAM); + } + public void onSomeSignal2(boolean Some_Param) + { + Log.i(TAG, "onSomeSignal2, will pass notification to all listeners"); + fireSomeSignal2(Some_Param); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java new file mode 100644 index 0000000..6e3493f --- /dev/null +++ b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java @@ -0,0 +1,180 @@ +package tbNames.tbNamesjniclient; + +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_api.INamEsEventListener; + +import tbNames.tbNames_android_client.NamEsClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NamEsJniClient extends AbstractNamEs implements INamEsEventListener +{ + + private static final String TAG = "NamEsJniClient"; + + private NamEsClient mMessengerClient = null; + + + private static String ModuleName = "tbNames.tbNamesjniservice.NamEsJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setSwitch(boolean Switch) + { + Log.i(TAG, "got request from ue, setSwitch" + (Switch)); + mMessengerClient.setSwitch(Switch); + } + @Override + public boolean getSwitch() + { + Log.i(TAG, "got request from ue, getSwitch"); + return mMessengerClient.getSwitch(); + } + + @Override + public void setSomeProperty(int SOME_PROPERTY) + { + Log.i(TAG, "got request from ue, setSomeProperty" + (SOME_PROPERTY)); + mMessengerClient.setSomeProperty(SOME_PROPERTY); + } + @Override + public int getSomeProperty() + { + Log.i(TAG, "got request from ue, getSomeProperty"); + return mMessengerClient.getSomeProperty(); + } + + @Override + public void setSomePoperty2(int Some_Poperty2) + { + Log.i(TAG, "got request from ue, setSomePoperty2" + (Some_Poperty2)); + mMessengerClient.setSomePoperty2(Some_Poperty2); + } + @Override + public int getSomePoperty2() + { + Log.i(TAG, "got request from ue, getSomePoperty2"); + return mMessengerClient.getSomePoperty2(); + } + + public void someFunction(boolean SOME_PARAM) + { + Log.v(TAG, "Blocking callsomeFunction - should not be used "); + mMessengerClient.someFunction(SOME_PARAM); + } + + public void someFunctionAsync(String callId, boolean SOME_PARAM){ + Log.v(TAG, "non blocking call someFunction "); + mMessengerClient.someFunctionAsync(SOME_PARAM).thenAccept(i -> { + nativeOnSomeFunctionResult(callId);}); + } + + //Should not be called directly, use someFunctionAsync(String callId, boolean SOME_PARAM) + public CompletableFuture someFunctionAsync(boolean SOME_PARAM) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.someFunctionAsync(SOME_PARAM); + } + public void someFunction2(boolean Some_Param) + { + Log.v(TAG, "Blocking callsomeFunction2 - should not be used "); + mMessengerClient.someFunction2(Some_Param); + } + + public void someFunction2Async(String callId, boolean Some_Param){ + Log.v(TAG, "non blocking call someFunction2 "); + mMessengerClient.someFunction2Async(Some_Param).thenAccept(i -> { + nativeOnSomeFunction2Result(callId);}); + } + + //Should not be called directly, use someFunction2Async(String callId, boolean Some_Param) + public CompletableFuture someFunction2Async(boolean Some_Param) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.someFunction2Async(Some_Param); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NamEsClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onSwitchChanged(boolean newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnSwitchChanged(newValue); + } + @Override + public void onSomePropertyChanged(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnSomePropertyChanged(newValue); + } + @Override + public void onSomePoperty2Changed(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnSomePoperty2Changed(newValue); + } + @Override + public void onSomeSignal(boolean SOME_PARAM) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal SOME_SIGNAL "+ " " + SOME_PARAM); + nativeOnSomeSignal(SOME_PARAM); + } + @Override + public void onSomeSignal2(boolean Some_Param) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal Some_Signal2 "+ " " + Some_Param); + nativeOnSomeSignal2(Some_Param); + } + private native void nativeOnSwitchChanged(boolean Switch); + private native void nativeOnSomePropertyChanged(int SOME_PROPERTY); + private native void nativeOnSomePoperty2Changed(int Some_Poperty2); + private native void nativeOnSomeSignal(boolean SOME_PARAM); + private native void nativeOnSomeSignal2(boolean Some_Param); + private native void nativeOnSomeFunctionResult(String callId); + private native void nativeOnSomeFunction2Result(String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java new file mode 100644 index 0000000..d58d8c8 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java @@ -0,0 +1,156 @@ +package tbNames.tbNamesjniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNames_api.INamEsEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NamEsJniService extends AbstractNamEs { + + + private final static String TAG = "NamEsJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NamEsJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setSwitch(boolean Switch) + { + Log.i(TAG, "request setSwitch called, will call native "); + nativeSetSwitch(Switch); + } + + @Override + public boolean getSwitch() + { + Log.i(TAG, "request getSwitch called, will call native "); + return nativeGetSwitch(); + } + + + @Override + public void setSomeProperty(int SOME_PROPERTY) + { + Log.i(TAG, "request setSomeProperty called, will call native "); + nativeSetSomeProperty(SOME_PROPERTY); + } + + @Override + public int getSomeProperty() + { + Log.i(TAG, "request getSomeProperty called, will call native "); + return nativeGetSomeProperty(); + } + + + @Override + public void setSomePoperty2(int Some_Poperty2) + { + Log.i(TAG, "request setSomePoperty2 called, will call native "); + nativeSetSomePoperty2(Some_Poperty2); + } + + @Override + public int getSomePoperty2() + { + Log.i(TAG, "request getSomePoperty2 called, will call native "); + return nativeGetSomePoperty2(); + } + + + // methods + + @Override + public void someFunction(boolean SOME_PARAM) { + Log.w(TAG, "request method someFunction called, will call native"); + nativeSomeFunction(SOME_PARAM); + } + + @Override + public CompletableFuture someFunctionAsync(boolean SOME_PARAM) { + return CompletableFuture.runAsync( + () -> { someFunction(SOME_PARAM); }, + executor); + } + + @Override + public void someFunction2(boolean Some_Param) { + Log.w(TAG, "request method someFunction2 called, will call native"); + nativeSomeFunction2(Some_Param); + } + + @Override + public CompletableFuture someFunction2Async(boolean Some_Param) { + return CompletableFuture.runAsync( + () -> { someFunction2(Some_Param); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetSwitch(boolean Switch); + private native boolean nativeGetSwitch(); + + private native void nativeSetSomeProperty(int SOME_PROPERTY); + private native int nativeGetSomeProperty(); + + private native void nativeSetSomePoperty2(int Some_Poperty2); + private native int nativeGetSomePoperty2(); + + // methods + private native void nativeSomeFunction(boolean SOME_PARAM); + private native void nativeSomeFunction2(boolean Some_Param); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onSwitchChanged(boolean newValue) + { + Log.i(TAG, "onSwitchChanged, will pass notification to all listeners"); + fireSwitchChanged(newValue); + } + public void onSomePropertyChanged(int newValue) + { + Log.i(TAG, "onSomePropertyChanged, will pass notification to all listeners"); + fireSomePropertyChanged(newValue); + } + public void onSomePoperty2Changed(int newValue) + { + Log.i(TAG, "onSomePoperty2Changed, will pass notification to all listeners"); + fireSomePoperty2Changed(newValue); + } + public void onSomeSignal(boolean SOME_PARAM) + { + Log.i(TAG, "onSomeSignal, will pass notification to all listeners"); + fireSomeSignal(SOME_PARAM); + } + public void onSomeSignal2(boolean Some_Param) + { + Log.i(TAG, "onSomeSignal2, will pass notification to all listeners"); + fireSomeSignal2(Some_Param); + } + +} diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java new file mode 100644 index 0000000..7e6b85e --- /dev/null +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbNames.tbNamesjniservice; + +import tbNames.tbNames_android_service.INamEsServiceFactory; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.AbstractNamEs; +import tbNames.tbNamesjniservice.NamEsJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NamEsJniServiceFactory thread for the system. This is a thread for + * NamEsJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NamEsJniServiceFactory extends HandlerThread implements INamEsServiceFactory +{ + private NamEsJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NamEsJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NamEsJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNamEs getServiceInstance() + { + if (jniService == null) + { + jniService = new NamEsJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NamEsJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NamEsJniServiceFactory INSTANCE = createInstance(); + } + + private NamEsJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NamEsJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NamEsJniServiceFactory t = new NamEsJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java new file mode 100644 index 0000000..2c89ef9 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbNames.tbNamesjniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_android_service.NamEsServiceAdapter; +import tbNames.tbNamesjniservice.NamEsJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NamEsJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NamEsJniStarter"; + + + + public static INamEs start(Context context) { + stop(context); + androidService = new Intent(context, NamEsServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NamEsJniServiceFactory factory = NamEsJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NamEsJniServiceFactory"); + return NamEsServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/build.gradle b/goldenmaster/tbNames/tbNamesserviceexample/build.gradle new file mode 100644 index 0000000..0b2ecbd --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbNames.tbNamesserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbNames.tbNamesserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbNames_api') + implementation project(':tbNames_android_messenger') + implementation project(':tbNames_android_service') + implementation project(':tbNames_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..deed5f3 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java new file mode 100644 index 0000000..5275c50 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java @@ -0,0 +1,253 @@ +package tbNames.tbNamesserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbNames.tbNames_android_service.NamEsServiceAdapter; +import tbNames.tbNames_android_service.NamEsServiceFactory; +import tbNames.tbNames_android_service.NamEsServiceStarter; + +//import message type and parcelabe types + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_impl.NamEsService; +import java.util.concurrent.CompletableFuture; + + + +public class TbNamesTestServiceApp extends Activity implements INamEsEventListener +{ + + private static final String TAG = "TbNamesTestServiceApp"; + static Intent stub_service = null; + + + private INamEs mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSwitch = new Button(this); + bSwitch.setText("Set Switch"); + bSwitch.setBackgroundColor(Color.GREEN); + + bSwitch.setOnClickListener(v -> { + boolean newSwitch = mBackend.getSwitch(); + //TODO increment + Log.i(TAG, "SET Switch" + newSwitch); + mBackend.setSwitch(newSwitch); + }); + propertyButtonsLine.addView(bSwitch); + Button bSomeProperty = new Button(this); + bSomeProperty.setText("Set SOME_PROPERTY"); + bSomeProperty.setBackgroundColor(Color.GREEN); + + bSomeProperty.setOnClickListener(v -> { + int newSomeProperty = mBackend.getSomeProperty(); + //TODO increment + Log.i(TAG, "SET SOME_PROPERTY" + newSomeProperty); + mBackend.setSomeProperty(newSomeProperty); + }); + propertyButtonsLine.addView(bSomeProperty); + Button bSomePoperty2 = new Button(this); + bSomePoperty2.setText("Set Some_Poperty2"); + bSomePoperty2.setBackgroundColor(Color.GREEN); + + bSomePoperty2.setOnClickListener(v -> { + int newSomePoperty2 = mBackend.getSomePoperty2(); + //TODO increment + Log.i(TAG, "SET Some_Poperty2" + newSomePoperty2); + mBackend.setSomePoperty2(newSomePoperty2); + }); + propertyButtonsLine.addView(bSomePoperty2); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSomeSignal = new Button(this); + bSomeSignal.setText("SOME_SIGNAL"); + + bSomeSignal.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal SOME_SIGNAL "); + boolean SOME_PARAM = true; + mBackend.fireSomeSignal(SOME_PARAM); + }); + bSomeSignal.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSomeSignal); + Button bSomeSignal2 = new Button(this); + bSomeSignal2.setText("Some_Signal2"); + + bSomeSignal2.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal Some_Signal2 "); + boolean Some_Param = true; + mBackend.fireSomeSignal2(Some_Param); + }); + bSomeSignal2.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSomeSignal2); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, NamEsServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = NamEsServiceAdapter.setService(NamEsServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onSwitchChanged(boolean newValue) + { + outputTextViewProp.setText("Property from service: Switch " + newValue); + Log.w(TAG, "Property from service: Switch " + newValue); + } + @Override + public void onSomePropertyChanged(int newValue) + { + outputTextViewProp.setText("Property from service: SOME_PROPERTY " + newValue); + Log.w(TAG, "Property from service: SOME_PROPERTY " + newValue); + } + @Override + public void onSomePoperty2Changed(int newValue) + { + outputTextViewProp.setText("Property from service: Some_Poperty2 " + newValue); + Log.w(TAG, "Property from service: Some_Poperty2 " + newValue); + } + @Override + public void onSomeSignal(boolean SOME_PARAM) + { + String text = "Signal SOME_SIGNAL "+ " " + SOME_PARAM; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSomeSignal2(boolean Some_Param) + { + String text = "Signal Some_Signal2 "+ " " + Some_Param; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/colors.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/strings.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..72b07c9 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbNamesTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/themes.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/gradle.properties b/goldenmaster/tbSame1/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbSame1/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbSame1/gradle/libs.versions.toml b/goldenmaster/tbSame1/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbSame1/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbSame1/settings.gradle b/goldenmaster/tbSame1/settings.gradle new file mode 100644 index 0000000..f9985e0 --- /dev/null +++ b/goldenmaster/tbSame1/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbSame1" +include ':tbSame1_android_service' +include ':tbSame1_android_client' +include ':tbSame1_android_messenger' +include ':tbSame1_impl' +include ':tbSame1_api' +include ':tbSame1_client_example' +include ':tbSame1serviceexample' \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle b/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle new file mode 100644 index 0000000..b5d26f0 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'tbSame1.tbSame1_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + implementation project(':tbSame1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/build.gradle b/goldenmaster/tbSame1/tbSame1_android_client/build.gradle new file mode 100644 index 0000000..81f8529 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame1" +version = "1.0.0" + +android { + namespace 'tbSame1.tbSame1_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + implementation project(':tbSame1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java new file mode 100644 index 0000000..f4473e1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java @@ -0,0 +1,339 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameEnum1InterfaceClient extends AbstractSameEnum1Interface implements ServiceConnection +{ + private static final String TAG = "SameEnum1InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Enum1 m_prop1 = Enum1.Value1; + + + public SameEnum1InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameEnum1InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameEnum1InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + onProp1(prop1); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + onProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + onSig1(param1); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameEnum1InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (m_prop1 != prop1) + { + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Enum1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + // methods + + + @Override + public Enum1 func1(Enum1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java new file mode 100644 index 0000000..670393b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java @@ -0,0 +1,471 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameEnum2InterfaceClient extends AbstractSameEnum2Interface implements ServiceConnection +{ + private static final String TAG = "SameEnum2InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Enum1 m_prop1 = Enum1.Value1; + private Enum2 m_prop2 = Enum2.Value1; + + + public SameEnum2InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameEnum2InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameEnum2InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + onProp1(prop1); + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + onProp2(prop2); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + onProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + onSig2(param1, param2); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameEnum2InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameEnum2InterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (m_prop1 != prop1) + { + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Enum1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (m_prop2 != prop2) + { + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(Enum2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + // methods + + + @Override + public Enum1 func1(Enum1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Enum1 func2(Enum1 param1, Enum2 param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + + data.putParcelable("param2", new Enum2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java new file mode 100644 index 0000000..7d2cb1d --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java @@ -0,0 +1,339 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameStruct1InterfaceClient extends AbstractSameStruct1Interface implements ServiceConnection +{ + private static final String TAG = "SameStruct1InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Struct1 m_prop1 = new Struct1(); + + + public SameStruct1InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameStruct1InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameStruct1InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + + Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + onProp1(prop1); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + + Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + + onProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + onSig1(param1); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameStruct1InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Struct1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Struct1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + // methods + + + @Override + public Struct1 func1(Struct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Struct1 result = bundle.getParcelable("result", Struct1Parcelable.class).getStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java new file mode 100644 index 0000000..6b0b66c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java @@ -0,0 +1,471 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameStruct2InterfaceClient extends AbstractSameStruct2Interface implements ServiceConnection +{ + private static final String TAG = "SameStruct2InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Struct2 m_prop1 = new Struct2(); + private Struct2 m_prop2 = new Struct2(); + + + public SameStruct2InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameStruct2InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameStruct2InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + + Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + onProp1(prop1); + + Struct2 prop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + onProp2(prop2); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + + Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + + Struct2 prop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + + onProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct2 param2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + onSig2(param1, param2); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameStruct2InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameStruct2InterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct2Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Struct2 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Struct2 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (! m_prop2.equals(prop2)) + { + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Struct2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(Struct2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public Struct2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + // methods + + + @Override + public Struct1 func1(Struct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Struct1 result = bundle.getParcelable("result", Struct1Parcelable.class).getStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Struct1 func2(Struct1 param1, Struct2 param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + + data.putParcelable("param2", new Struct2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Struct1 result = bundle.getParcelable("result", Struct1Parcelable.class).getStruct1(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java new file mode 100644 index 0000000..3ee664a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java @@ -0,0 +1,251 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame1.tbSame1_android_client; + +import tbSame1.tbSame1_android_client.SameEnum1InterfaceClient; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameEnum1InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum1InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameEnum1InterfaceClient testedClient; + private ISameEnum1InterfaceEventListener listenerMock = mock(ISameEnum1InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameEnum1InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameEnum1InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum1InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameEnum1InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameEnum1InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame1.tbSame1_android_service", "tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum1InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } + + @Test + public void setPropertyRequestprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1(testparam1); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameEnum1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java new file mode 100644 index 0000000..ca8fcf2 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java @@ -0,0 +1,355 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame1.tbSame1_android_client; + +import tbSame1.tbSame1_android_client.SameEnum2InterfaceClient; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameEnum2InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum2InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameEnum2InterfaceClient testedClient; + private ISameEnum2InterfaceEventListener listenerMock = mock(ISameEnum2InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameEnum2InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameEnum2InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameEnum2InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameEnum2InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame1.tbSame1_android_service", "tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + Enum2 testprop2 = Enum2.Value2; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } + + @Test + public void setPropertyRequestprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + Enum2 testprop2 = Enum2.Value2; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + } + + @Test + public void setPropertyRequestprop2() + { + Enum2 testprop2 = Enum2.Value2; + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1(testparam1); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum2 testparam2 = Enum2.Value2; + data.putParcelable("param2", new Enum2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2(testparam1, testparam2); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum2 testparam2 = Enum2.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + + Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java new file mode 100644 index 0000000..dbc010e --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java @@ -0,0 +1,251 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame1.tbSame1_android_client; + +import tbSame1.tbSame1_android_client.SameStruct1InterfaceClient; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameStruct1InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct1InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameStruct1InterfaceClient testedClient; + private ISameStruct1InterfaceEventListener listenerMock = mock(ISameStruct1InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameStruct1InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameStruct1InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct1InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameStruct1InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameStruct1InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame1.tbSame1_android_service", "tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct1InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Struct1 testprop1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("prop1", new Struct1Parcelable(testprop1)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Struct1 testprop1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("prop1", new Struct1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); + } + + @Test + public void setPropertyRequestprop1() + { + Struct1 testprop1 = TbSame1TestHelper.makeTestStruct1(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(Struct1.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + Struct1 expectedResult = TbSame1TestHelper.makeTestStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameStruct1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Struct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java new file mode 100644 index 0000000..d8735f1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java @@ -0,0 +1,355 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame1.tbSame1_android_client; + +import tbSame1.tbSame1_android_client.SameStruct2InterfaceClient; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameStruct2InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct2InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameStruct2InterfaceClient testedClient; + private ISameStruct2InterfaceEventListener listenerMock = mock(ISameStruct2InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameStruct2InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameStruct2InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameStruct2InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameStruct2InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame1.tbSame1_android_service", "tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Struct2 testprop1 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("prop1", new Struct2Parcelable(testprop1)); + Struct2 testprop2 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("prop2", new Struct2Parcelable(testprop2)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Struct2 testprop1 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("prop1", new Struct2Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); + } + + @Test + public void setPropertyRequestprop1() + { + Struct2 testprop1 = TbSame1TestHelper.makeTestStruct2(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + Struct2 testprop2 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("prop2", new Struct2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); + } + + @Test + public void setPropertyRequestprop2() + { + Struct2 testprop2 = TbSame1TestHelper.makeTestStruct2(); + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(Struct1.class)); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct2 testparam2 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("param2", new Struct2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2( any(Struct1.class), any(Struct2.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + Struct1 expectedResult = TbSame1TestHelper.makeTestStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Struct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + Struct2 testparam2 = TbSame1TestHelper.makeTestStruct2(); + Struct1 expectedResult = TbSame1TestHelper.makeTestStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + + Struct2 receivedparam2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Struct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/additions.gradle b/goldenmaster/tbSame1/tbSame1_android_messenger/additions.gradle new file mode 100644 index 0000000..abdc4f2 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbSame1.tbSame1_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/build.gradle b/goldenmaster/tbSame1/tbSame1_android_messenger/build.gradle new file mode 100644 index 0000000..e086f29 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame1" +version = "1.0.0" + +android { + namespace 'tbSame1.tbSame1_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum1Parcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum1Parcelable.java new file mode 100644 index 0000000..223cdd2 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum1Parcelable.java @@ -0,0 +1,69 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.Enum1; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum1Parcelable implements Parcelable { + + public Enum1 data; + + public Enum1Parcelable(Enum1 data) { + this.data = data; + } + + public Enum1 getEnum1() + { + return data; + } + + protected Enum1Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum1.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum1Parcelable createFromParcel(Parcel in) { + return new Enum1Parcelable(in); + } + + @Override + public Enum1Parcelable[] newArray(int size) { + return new Enum1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum1Parcelable[] wrapArray(Enum1[] enums) { + if (enums == null) return null; + Enum1Parcelable[] result = new Enum1Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum1Parcelable(enums[i]); + } + return result; + } + + public static Enum1[] unwrapArray(Enum1Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum1[] out = new Enum1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum2Parcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum2Parcelable.java new file mode 100644 index 0000000..1f07bd3 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Enum2Parcelable.java @@ -0,0 +1,69 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.Enum2; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum2Parcelable implements Parcelable { + + public Enum2 data; + + public Enum2Parcelable(Enum2 data) { + this.data = data; + } + + public Enum2 getEnum2() + { + return data; + } + + protected Enum2Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum2.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum2Parcelable createFromParcel(Parcel in) { + return new Enum2Parcelable(in); + } + + @Override + public Enum2Parcelable[] newArray(int size) { + return new Enum2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum2Parcelable[] wrapArray(Enum2[] enums) { + if (enums == null) return null; + Enum2Parcelable[] result = new Enum2Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum2Parcelable(enums[i]); + } + return result; + } + + public static Enum2[] unwrapArray(Enum2Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum2[] out = new Enum2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceMessageType.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceMessageType.java new file mode 100644 index 0000000..4a32001 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceMessageType.java @@ -0,0 +1,35 @@ +package tbSame1.tbSame1_android_messenger; + +public enum SameEnum1InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + SIG_Sig1(5), + RPC_Func1Req(6), + RPC_Func1Resp(7), + SameEnum1InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameEnum1InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameEnum1InterfaceMessageType fromInteger(int value) + { + for (SameEnum1InterfaceMessageType event : SameEnum1InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameEnum1InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceMessageType.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceMessageType.java new file mode 100644 index 0000000..fb1003a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceMessageType.java @@ -0,0 +1,40 @@ +package tbSame1.tbSame1_android_messenger; + +public enum SameEnum2InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + SIG_Sig1(7), + SIG_Sig2(8), + RPC_Func1Req(9), + RPC_Func1Resp(10), + RPC_Func2Req(11), + RPC_Func2Resp(12), + SameEnum2InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameEnum2InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameEnum2InterfaceMessageType fromInteger(int value) + { + for (SameEnum2InterfaceMessageType event : SameEnum2InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameEnum2InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceMessageType.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceMessageType.java new file mode 100644 index 0000000..8ce26ea --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceMessageType.java @@ -0,0 +1,35 @@ +package tbSame1.tbSame1_android_messenger; + +public enum SameStruct1InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + SIG_Sig1(5), + RPC_Func1Req(6), + RPC_Func1Resp(7), + SameStruct1InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameStruct1InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameStruct1InterfaceMessageType fromInteger(int value) + { + for (SameStruct1InterfaceMessageType event : SameStruct1InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameStruct1InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceMessageType.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceMessageType.java new file mode 100644 index 0000000..7f0a4f7 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceMessageType.java @@ -0,0 +1,40 @@ +package tbSame1.tbSame1_android_messenger; + +public enum SameStruct2InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + SIG_Sig1(7), + SIG_Sig2(8), + RPC_Func1Req(9), + RPC_Func1Resp(10), + RPC_Func2Req(11), + RPC_Func2Resp(12), + SameStruct2InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameStruct2InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameStruct2InterfaceMessageType fromInteger(int value) + { + for (SameStruct2InterfaceMessageType event : SameStruct2InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameStruct2InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct1Parcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct1Parcelable.java new file mode 100644 index 0000000..929c9bd --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct1Parcelable.java @@ -0,0 +1,69 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.Struct1; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct1Parcelable implements Parcelable { + + public Struct1 data; + + public Struct1Parcelable(Struct1 data) { + this.data = new Struct1(data); + } + + public Struct1 getStruct1() + { + return new Struct1(data); + } + + protected Struct1Parcelable(Parcel in) { + this.data = new Struct1(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + data.field3 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct1Parcelable createFromParcel(Parcel in) { + return new Struct1Parcelable(in); + } + + @Override + public Struct1Parcelable[] newArray(int size) { + return new Struct1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + dest.writeInt(data.field3); + + + } + public static Struct1Parcelable[] wrapArray(Struct1[] structs) { + if (structs == null) return null; + Struct1Parcelable[] out = new Struct1Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct1Parcelable(structs[i]); + } + return out; + } + + public static Struct1[] unwrapArray(Struct1Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct1[] out = new Struct1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct2Parcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct2Parcelable.java new file mode 100644 index 0000000..4e6cc4d --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/Struct2Parcelable.java @@ -0,0 +1,69 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.Struct2; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct2Parcelable implements Parcelable { + + public Struct2 data; + + public Struct2Parcelable(Struct2 data) { + this.data = new Struct2(data); + } + + public Struct2 getStruct2() + { + return new Struct2(data); + } + + protected Struct2Parcelable(Parcel in) { + this.data = new Struct2(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + data.field3 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct2Parcelable createFromParcel(Parcel in) { + return new Struct2Parcelable(in); + } + + @Override + public Struct2Parcelable[] newArray(int size) { + return new Struct2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + dest.writeInt(data.field3); + + + } + public static Struct2Parcelable[] wrapArray(Struct2[] structs) { + if (structs == null) return null; + Struct2Parcelable[] out = new Struct2Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct2Parcelable(structs[i]); + } + return out; + } + + public static Struct2[] unwrapArray(Struct2Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct2[] out = new Struct2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle b/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle new file mode 100644 index 0000000..afb2d60 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbSame1.tbSame1_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + implementation project(':tbSame1_impl') + implementation project(':tbSame1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/build.gradle b/goldenmaster/tbSame1/tbSame1_android_service/build.gradle new file mode 100644 index 0000000..6c902af --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame1" +version = "1.0.0" + + +android { + namespace 'tbSame1.tbSame1_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + implementation project(':tbSame1_impl') + implementation project(':tbSame1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbSame1/tbSame1_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9936a0b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum1InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum1InterfaceServiceFactory.java new file mode 100644 index 0000000..41040ce --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum1InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame1.tbSame1_android_service; +import tbSame1.tbSame1_api.ISameEnum1Interface; + + +public interface ISameEnum1InterfaceServiceFactory { + public ISameEnum1Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum2InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum2InterfaceServiceFactory.java new file mode 100644 index 0000000..a70f5d6 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameEnum2InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame1.tbSame1_android_service; +import tbSame1.tbSame1_api.ISameEnum2Interface; + + +public interface ISameEnum2InterfaceServiceFactory { + public ISameEnum2Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct1InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct1InterfaceServiceFactory.java new file mode 100644 index 0000000..467a245 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct1InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame1.tbSame1_android_service; +import tbSame1.tbSame1_api.ISameStruct1Interface; + + +public interface ISameStruct1InterfaceServiceFactory { + public ISameStruct1Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct2InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct2InterfaceServiceFactory.java new file mode 100644 index 0000000..549412a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/ISameStruct2InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame1.tbSame1_android_service; +import tbSame1.tbSame1_api.ISameStruct2Interface; + + +public interface ISameStruct2InterfaceServiceFactory { + public ISameStruct2Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java new file mode 100644 index 0000000..220789a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java @@ -0,0 +1,300 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameEnum1InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameEnum1InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameEnum1Interface mBackendService; + private static ISameEnum1InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameEnum1InterfaceServiceAdapter() + { + } + + public static ISameEnum1Interface setService(ISameEnum1InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum1InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameEnum1InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameEnum1InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameEnum1InterfaceMessageType.fromInteger(msg.what) != SameEnum1InterfaceMessageType.REGISTER_CLIENT + && SameEnum1InterfaceMessageType.fromInteger(msg.what) != SameEnum1InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameEnum1InterfaceMessageType" + SameEnum1InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameEnum1InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + mBackendService.setProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameEnum1InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Enum1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Enum1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Enum1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java new file mode 100644 index 0000000..2351c69 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import tbSame1.tbSame1_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum1InterfaceServiceFactory thread for the system. This is a thread for + * SameEnum1InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum1InterfaceServiceFactory extends HandlerThread implements ISameEnum1InterfaceServiceFactory +{ + private SameEnum1InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum1InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum1Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameEnum1InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum1InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameEnum1InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameEnum1InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum1InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum1InterfaceServiceFactory t = new SameEnum1InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java new file mode 100644 index 0000000..25ad342 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter; +import tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame1.tbSame1_impl; . +public class SameEnum1InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum1InterfaceStarter"; + + + + public static ISameEnum1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum1InterfaceServiceFactory factory = SameEnum1InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java new file mode 100644 index 0000000..62c7e10 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java @@ -0,0 +1,369 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameEnum2InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameEnum2InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameEnum2Interface mBackendService; + private static ISameEnum2InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameEnum2InterfaceServiceAdapter() + { + } + + public static ISameEnum2Interface setService(ISameEnum2InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum2InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameEnum2InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameEnum2InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameEnum2InterfaceMessageType.fromInteger(msg.what) != SameEnum2InterfaceMessageType.REGISTER_CLIENT + && SameEnum2InterfaceMessageType.fromInteger(msg.what) != SameEnum2InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameEnum2InterfaceMessageType" + SameEnum2InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameEnum2InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + mBackendService.setProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameEnum2InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + + Enum1 result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = SameEnum2InterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Enum1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + Enum2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Enum1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(Enum2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Enum1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(Enum1 param1, Enum2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + + data.putParcelable("param2", new Enum2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java new file mode 100644 index 0000000..0d51f92 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import tbSame1.tbSame1_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum2InterfaceServiceFactory thread for the system. This is a thread for + * SameEnum2InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum2InterfaceServiceFactory extends HandlerThread implements ISameEnum2InterfaceServiceFactory +{ + private SameEnum2InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum2InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum2Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameEnum2InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum2InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameEnum2InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameEnum2InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum2InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum2InterfaceServiceFactory t = new SameEnum2InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java new file mode 100644 index 0000000..3dabc4d --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter; +import tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame1.tbSame1_impl; . +public class SameEnum2InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum2InterfaceStarter"; + + + + public static ISameEnum2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum2InterfaceServiceFactory factory = SameEnum2InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java new file mode 100644 index 0000000..cb0ab1a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java @@ -0,0 +1,300 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameStruct1InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameStruct1InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameStruct1Interface mBackendService; + private static ISameStruct1InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameStruct1InterfaceServiceAdapter() + { + } + + public static ISameStruct1Interface setService(ISameStruct1InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct1InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameStruct1InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameStruct1InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameStruct1InterfaceMessageType.fromInteger(msg.what) != SameStruct1InterfaceMessageType.REGISTER_CLIENT + && SameStruct1InterfaceMessageType.fromInteger(msg.what) != SameStruct1InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameStruct1InterfaceMessageType" + SameStruct1InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameStruct1InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + mBackendService.setProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameStruct1InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Struct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Struct1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Struct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Struct1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Struct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java new file mode 100644 index 0000000..24fe613 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import tbSame1.tbSame1_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct1InterfaceServiceFactory thread for the system. This is a thread for + * SameStruct1InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct1InterfaceServiceFactory extends HandlerThread implements ISameStruct1InterfaceServiceFactory +{ + private SameStruct1InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct1InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct1Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameStruct1InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct1InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameStruct1InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameStruct1InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct1InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct1InterfaceServiceFactory t = new SameStruct1InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java new file mode 100644 index 0000000..ddd614c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter; +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame1.tbSame1_impl; . +public class SameStruct1InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct1InterfaceStarter"; + + + + public static ISameStruct1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct1InterfaceServiceFactory factory = SameStruct1InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java new file mode 100644 index 0000000..44395f2 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java @@ -0,0 +1,369 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameStruct2InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameStruct2InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameStruct2Interface mBackendService; + private static ISameStruct2InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameStruct2InterfaceServiceAdapter() + { + } + + public static ISameStruct2Interface setService(ISameStruct2InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct2InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameStruct2InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameStruct2InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameStruct2InterfaceMessageType.fromInteger(msg.what) != SameStruct2InterfaceMessageType.REGISTER_CLIENT + && SameStruct2InterfaceMessageType.fromInteger(msg.what) != SameStruct2InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameStruct2InterfaceMessageType" + SameStruct2InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameStruct2InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 prop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + mBackendService.setProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameStruct2InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Struct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct2 param2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + + Struct1 result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = SameStruct2InterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Struct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Struct2 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Struct2Parcelable(prop1)); + Struct2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new Struct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Struct2 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct2Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(Struct2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Struct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Struct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(Struct1 param1, Struct2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + + data.putParcelable("param2", new Struct2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java new file mode 100644 index 0000000..c184793 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import tbSame1.tbSame1_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct2InterfaceServiceFactory thread for the system. This is a thread for + * SameStruct2InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct2InterfaceServiceFactory extends HandlerThread implements ISameStruct2InterfaceServiceFactory +{ + private SameStruct2InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct2InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct2Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameStruct2InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct2InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameStruct2InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameStruct2InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct2InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct2InterfaceServiceFactory t = new SameStruct2InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java new file mode 100644 index 0000000..f0dae12 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter; +import tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame1.tbSame1_impl; . +public class SameStruct2InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct2InterfaceStarter"; + + + + public static ISameStruct2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct2InterfaceServiceFactory factory = SameStruct2InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..5e916eb --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -0,0 +1,295 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameEnum1InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum1InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameEnum1InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameEnum1InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameEnum1Interface backendServiceMock = mock(ISameEnum1Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameEnum1InterfaceServiceFactory serviceFactory = mock(ISameEnum1InterfaceServiceFactory.class); + private ISameEnum1InterfaceMessageGetter clientMessagesStorage = mock(ISameEnum1InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameEnum1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameEnum1InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Enum1 initprop1 = Enum1.Value2; + when(backendServiceMock.getProp1()).thenReturn(initprop1); + + + Message registerMsg = Message.obtain(null, SameEnum1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + assertEquals(receivedprop1, initprop1); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameEnum1InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameEnum1InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameEnum1InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1(testprop1); + + } + + @Test + public void whenNotifiedprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() + { + Enum1 testparam1 = Enum1.Value2; + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func1(testparam1)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1(testparam1); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..aad67b0 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -0,0 +1,396 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameEnum2InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum2InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameEnum2InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameEnum2InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameEnum2Interface backendServiceMock = mock(ISameEnum2Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameEnum2InterfaceServiceFactory serviceFactory = mock(ISameEnum2InterfaceServiceFactory.class); + private ISameEnum2InterfaceMessageGetter clientMessagesStorage = mock(ISameEnum2InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameEnum2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameEnum2InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Enum1 initprop1 = Enum1.Value2; + when(backendServiceMock.getProp1()).thenReturn(initprop1); + Enum2 initprop2 = Enum2.Value2; + when(backendServiceMock.getProp2()).thenReturn(initprop2); + + + Message registerMsg = Message.obtain(null, SameEnum2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + assertEquals(receivedprop1, initprop1); + assertEquals(receivedprop2, initprop2); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameEnum2InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameEnum2InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameEnum2InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1(testprop1); + + } + + @Test + public void whenNotifiedprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + Enum2 testprop2 = Enum2.Value2; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2(testprop2); + + } + + @Test + public void whenNotifiedprop2() + { + Enum2 testprop2 = Enum2.Value2; + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() + { + Enum1 testparam1 = Enum1.Value2; + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + Enum1 testparam1 = Enum1.Value2; + Enum2 testparam2 = Enum2.Value2; + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + + Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedparam2, testparam2); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func1(testparam1)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1(testparam1); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum2 testparam2 = Enum2.Value2; + data.putParcelable("param2", new Enum2Parcelable(testparam2)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func2(testparam1, testparam2)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2(testparam1, testparam2); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..79eb221 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -0,0 +1,296 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameStruct1InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct1InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameStruct1InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameStruct1InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameStruct1Interface backendServiceMock = mock(ISameStruct1Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameStruct1InterfaceServiceFactory serviceFactory = mock(ISameStruct1InterfaceServiceFactory.class); + private ISameStruct1InterfaceMessageGetter clientMessagesStorage = mock(ISameStruct1InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameStruct1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameStruct1InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Struct1 initprop1 = new Struct1(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + + + Message registerMsg = Message.obtain(null, SameStruct1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameStruct1InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameStruct1InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameStruct1InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Struct1 testprop1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("prop1", new Struct1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(Struct1.class)); + + } + + @Test + public void whenNotifiedprop1() + { + Struct1 testprop1 = TbSame1TestHelper.makeTestStruct1(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() + { + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct1 returnedValue = TbSame1TestHelper.makeTestStruct1(); + + + when(backendServiceMock.func1( any(Struct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(Struct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + Struct1 receivedByClient = resp_data.getParcelable("result", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..bbea9d6 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -0,0 +1,398 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.TbSame1TestHelper; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameStruct2InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct2InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameStruct2InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameStruct2InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameStruct2Interface backendServiceMock = mock(ISameStruct2Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameStruct2InterfaceServiceFactory serviceFactory = mock(ISameStruct2InterfaceServiceFactory.class); + private ISameStruct2InterfaceMessageGetter clientMessagesStorage = mock(ISameStruct2InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameStruct2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameStruct2InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Struct2 initprop1 = new Struct2(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + Struct2 initprop2 = new Struct2(); + //TODO fill fields + when(backendServiceMock.getProp2()).thenReturn(initprop2); + + + Message registerMsg = Message.obtain(null, SameStruct2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + + Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + // assertEquals(receivedprop2, initprop2); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameStruct2InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameStruct2InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameStruct2InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Struct2 testprop1 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("prop1", new Struct2Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(Struct2.class)); + + } + + @Test + public void whenNotifiedprop1() + { + Struct2 testprop1 = TbSame1TestHelper.makeTestStruct2(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + Struct2 testprop2 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("prop2", new Struct2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2( any(Struct2.class)); + + } + + @Test + public void whenNotifiedprop2() + { + Struct2 testprop2 = TbSame1TestHelper.makeTestStruct2(); + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() + { + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + Struct2 testparam2 = TbSame1TestHelper.makeTestStruct2(); + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + + Struct2 receivedparam2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedparam2, testparam2); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct1 returnedValue = TbSame1TestHelper.makeTestStruct1(); + + + when(backendServiceMock.func1( any(Struct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(Struct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + Struct1 receivedByClient = resp_data.getParcelable("result", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Struct1 testparam1 = TbSame1TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct2 testparam2 = TbSame1TestHelper.makeTestStruct2(); + data.putParcelable("param2", new Struct2Parcelable(testparam2)); + Struct1 returnedValue = TbSame1TestHelper.makeTestStruct1(); + + + when(backendServiceMock.func2( any(Struct1.class), any(Struct2.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2( any(Struct1.class), any(Struct2.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + Struct1 receivedByClient = resp_data.getParcelable("result", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_api/additions.gradle b/goldenmaster/tbSame1/tbSame1_api/additions.gradle new file mode 100644 index 0000000..8a4fff4 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'tbSame1.tbSame1_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_api/build.gradle b/goldenmaster/tbSame1/tbSame1_api/build.gradle new file mode 100644 index 0000000..de2541b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "tbSame1" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java new file mode 100644 index 0000000..1d45ded --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java @@ -0,0 +1,43 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum1Interface; +//TODO imported/extern modules +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameEnum1Interface implements ISameEnum1Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameEnum1InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameEnum1InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Enum1 newValue) { + for (ISameEnum1InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireSig1(Enum1 param1) { + for (ISameEnum1InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameEnum1InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java new file mode 100644 index 0000000..7a45fcd --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java @@ -0,0 +1,57 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum2Interface; +//TODO imported/extern modules +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameEnum2Interface implements ISameEnum2Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameEnum2InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameEnum2InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Enum1 newValue) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(Enum2 newValue) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireSig1(Enum1 param1) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(Enum1 param1, Enum2 param2) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java new file mode 100644 index 0000000..9991769 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java @@ -0,0 +1,43 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +//TODO imported/extern modules +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameStruct1Interface implements ISameStruct1Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameStruct1InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameStruct1InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Struct1 newValue) { + for (ISameStruct1InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireSig1(Struct1 param1) { + for (ISameStruct1InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameStruct1InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java new file mode 100644 index 0000000..9e6266a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java @@ -0,0 +1,57 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct2Interface; +//TODO imported/extern modules +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameStruct2Interface implements ISameStruct2Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameStruct2InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameStruct2InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Struct2 newValue) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(Struct2 newValue) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireSig1(Struct1 param1) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(Struct1 param1, Struct2 param2) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum1.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum1.java new file mode 100644 index 0000000..9b2bfe5 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum1.java @@ -0,0 +1,28 @@ +package tbSame1.tbSame1_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum1 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2); + + private final int value; + + Enum1(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum1 fromValue(int value) { + for (Enum1 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum2.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum2.java new file mode 100644 index 0000000..37e13ce --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Enum2.java @@ -0,0 +1,28 @@ +package tbSame1.tbSame1_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum2 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2); + + private final int value; + + Enum2(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum2 fromValue(int value) { + for (Enum2 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1Interface.java new file mode 100644 index 0000000..8184866 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1Interface.java @@ -0,0 +1,27 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameEnum1Interface { + // properties + void setProp1(Enum1 prop1); + Enum1 getProp1(); + void fireProp1Changed(Enum1 newValue); + + // methods + Enum1 func1(Enum1 param1); + CompletableFuture func1Async(Enum1 param1); + public void fireSig1(Enum1 param1); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameEnum1InterfaceEventListener listener); + void removeEventListener(ISameEnum1InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1InterfaceEventListener.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1InterfaceEventListener.java new file mode 100644 index 0000000..0350958 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum1InterfaceEventListener.java @@ -0,0 +1,11 @@ +package tbSame1.tbSame1_api; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + public interface ISameEnum1InterfaceEventListener { + void onProp1Changed(Enum1 newValue); + void onSig1(Enum1 param1); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2Interface.java new file mode 100644 index 0000000..3f90934 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2Interface.java @@ -0,0 +1,34 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameEnum2Interface { + // properties + void setProp1(Enum1 prop1); + Enum1 getProp1(); + void fireProp1Changed(Enum1 newValue); + + void setProp2(Enum2 prop2); + Enum2 getProp2(); + void fireProp2Changed(Enum2 newValue); + + // methods + Enum1 func1(Enum1 param1); + CompletableFuture func1Async(Enum1 param1); + Enum1 func2(Enum1 param1, Enum2 param2); + CompletableFuture func2Async(Enum1 param1, Enum2 param2); + public void fireSig1(Enum1 param1); + public void fireSig2(Enum1 param1, Enum2 param2); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameEnum2InterfaceEventListener listener); + void removeEventListener(ISameEnum2InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2InterfaceEventListener.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2InterfaceEventListener.java new file mode 100644 index 0000000..9c894a9 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameEnum2InterfaceEventListener.java @@ -0,0 +1,13 @@ +package tbSame1.tbSame1_api; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + public interface ISameEnum2InterfaceEventListener { + void onProp1Changed(Enum1 newValue); + void onProp2Changed(Enum2 newValue); + void onSig1(Enum1 param1); + void onSig2(Enum1 param1, Enum2 param2); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1Interface.java new file mode 100644 index 0000000..dc0eefe --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1Interface.java @@ -0,0 +1,27 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameStruct1Interface { + // properties + void setProp1(Struct1 prop1); + Struct1 getProp1(); + void fireProp1Changed(Struct1 newValue); + + // methods + Struct1 func1(Struct1 param1); + CompletableFuture func1Async(Struct1 param1); + public void fireSig1(Struct1 param1); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameStruct1InterfaceEventListener listener); + void removeEventListener(ISameStruct1InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1InterfaceEventListener.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1InterfaceEventListener.java new file mode 100644 index 0000000..c73bd20 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct1InterfaceEventListener.java @@ -0,0 +1,11 @@ +package tbSame1.tbSame1_api; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + public interface ISameStruct1InterfaceEventListener { + void onProp1Changed(Struct1 newValue); + void onSig1(Struct1 param1); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2Interface.java new file mode 100644 index 0000000..c4c31cd --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2Interface.java @@ -0,0 +1,34 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameStruct2Interface { + // properties + void setProp1(Struct2 prop1); + Struct2 getProp1(); + void fireProp1Changed(Struct2 newValue); + + void setProp2(Struct2 prop2); + Struct2 getProp2(); + void fireProp2Changed(Struct2 newValue); + + // methods + Struct1 func1(Struct1 param1); + CompletableFuture func1Async(Struct1 param1); + Struct1 func2(Struct1 param1, Struct2 param2); + CompletableFuture func2Async(Struct1 param1, Struct2 param2); + public void fireSig1(Struct1 param1); + public void fireSig2(Struct1 param1, Struct2 param2); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameStruct2InterfaceEventListener listener); + void removeEventListener(ISameStruct2InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2InterfaceEventListener.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2InterfaceEventListener.java new file mode 100644 index 0000000..45b25f1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/ISameStruct2InterfaceEventListener.java @@ -0,0 +1,13 @@ +package tbSame1.tbSame1_api; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + public interface ISameStruct2InterfaceEventListener { + void onProp1Changed(Struct2 newValue); + void onProp2Changed(Struct2 newValue); + void onSig1(Struct1 param1); + void onSig2(Struct1 param1, Struct2 param2); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct1.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct1.java new file mode 100644 index 0000000..f2d5cb1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct1.java @@ -0,0 +1,55 @@ +package tbSame1.tbSame1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct1 { + + public Struct1(int field1, int field2, int field3) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public Struct1() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + @JsonProperty("field3") + public int field3; + + public Struct1(Struct1 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + this.field3 = other.field3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct1)) return false; + Struct1 other = (Struct1) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2 + && this.field3 == other.field3; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + result = 31 * result + Integer.hashCode(field3); + return result; + } + + +} diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct2.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct2.java new file mode 100644 index 0000000..725fd12 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/Struct2.java @@ -0,0 +1,55 @@ +package tbSame1.tbSame1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct2 { + + public Struct2(int field1, int field2, int field3) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public Struct2() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + @JsonProperty("field3") + public int field3; + + public Struct2(Struct2 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + this.field3 = other.field3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct2)) return false; + Struct2 other = (Struct2) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2 + && this.field3 == other.field3; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + result = 31 * result + Integer.hashCode(field3); + return result; + } + + +} diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java new file mode 100644 index 0000000..50f5ad9 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java @@ -0,0 +1,29 @@ +package tbSame1.tbSame1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class TbSame1TestHelper +{ + + static public Struct1 makeTestStruct1() + { + Struct1 testStruct = new Struct1(); + testStruct.field1 = 1; + testStruct.field2 = 1; + testStruct.field3 = 1; + return testStruct; + } + + static public Struct2 makeTestStruct2() + { + Struct2 testStruct = new Struct2(); + testStruct.field1 = 1; + testStruct.field2 = 1; + testStruct.field3 = 1; + return testStruct; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/build.gradle b/goldenmaster/tbSame1/tbSame1_client_example/build.gradle new file mode 100644 index 0000000..7e359c1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbSame1.tbSame1_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbSame1.tbSame1_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbSame1_api') + implementation project(':tbSame1_android_messenger') + implementation project(':tbSame1_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a1fb2c5 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java new file mode 100644 index 0000000..28a5d61 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java @@ -0,0 +1,237 @@ +package tbSame1.tbSame1_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbSame1.tbSame1_android_client.SameStruct1InterfaceClient; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbSame1TestClientApp extends Activity implements ISameStruct1InterfaceEventListener +{ + + private static final String TAG = "TbSame1TestClientApp"; + + private SameStruct1InterfaceClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbSame1.tbSame1serviceexample.TbSame1TestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + Struct1 newProp1 = new Struct1(mClient.getProp1();); + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mClient.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bFunc1 = new Button(this); + bFunc1.setText("func1"); + + bFunc1.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func1 "); + Struct1 param1 = new Struct1(); + CompletableFuture method_res + = mClient.func1Async(param1).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func1 result "+ i); + return i; + }); + }); + bFunc1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc1); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new SameStruct1InterfaceClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onProp1Changed(Struct1 newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/colors.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/strings.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..65203b3 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbSame1TestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/themes.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_impl/additions.gradle b/goldenmaster/tbSame1/tbSame1_impl/additions.gradle new file mode 100644 index 0000000..bf24bc2 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbSame1.tbSame1_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_impl/build.gradle b/goldenmaster/tbSame1/tbSame1_impl/build.gradle new file mode 100644 index 0000000..f0c65aa --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame1" +version = "1.0.0" + +android { + namespace 'tbSame1.tbSame1_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java new file mode 100644 index 0000000..e422263 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java @@ -0,0 +1,89 @@ +package tbSame1.tbSame1_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameEnum1InterfaceService extends AbstractSameEnum1Interface { + + private final static String TAG = "SameEnum1InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Enum1 m_prop1 = Enum1.Value1; + + public SameEnum1InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java new file mode 100644 index 0000000..4046e67 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java @@ -0,0 +1,133 @@ +package tbSame1.tbSame1_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameEnum2InterfaceService extends AbstractSameEnum2Interface { + + private final static String TAG = "SameEnum2InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Enum1 m_prop1 = Enum1.Value1; + private Enum2 m_prop2 = Enum2.Value1; + + public SameEnum2InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Enum1 func2(Enum1 param1, Enum2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java new file mode 100644 index 0000000..57b4c15 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java @@ -0,0 +1,89 @@ +package tbSame1.tbSame1_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameStruct1InterfaceService extends AbstractSameStruct1Interface { + + private final static String TAG = "SameStruct1InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Struct1 m_prop1 = new Struct1(); + + public SameStruct1InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Struct1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new Struct1(); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Struct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java new file mode 100644 index 0000000..bc49388 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java @@ -0,0 +1,133 @@ +package tbSame1.tbSame1_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameStruct2InterfaceService extends AbstractSameStruct2Interface { + + private final static String TAG = "SameStruct2InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Struct2 m_prop1 = new Struct2(); + private Struct2 m_prop2 = new Struct2(); + + public SameStruct2InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Struct2 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public Struct2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new Struct1(); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Struct1 func2(Struct1 param1, Struct2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return new Struct1(); + } + + @Override + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Struct2 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(Struct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java new file mode 100644 index 0000000..79395db --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java @@ -0,0 +1,118 @@ +package tbSame1.tbSame1jniclient; + +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; + +import tbSame1.tbSame1_android_client.SameEnum1InterfaceClient; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameEnum1InterfaceJniClient extends AbstractSameEnum1Interface implements ISameEnum1InterfaceEventListener +{ + + private static final String TAG = "SameEnum1InterfaceJniClient"; + + private SameEnum1InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame1.tbSame1jniservice.SameEnum1InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Enum1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + public Enum1 func1(Enum1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Enum1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Enum1 param1) + public CompletableFuture func1Async(Enum1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum1InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + private native void nativeOnProp1Changed(Enum1 prop1); + private native void nativeOnSig1(Enum1 param1); + private native void nativeOnFunc1Result(Enum1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java new file mode 100644 index 0000000..1542776 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java @@ -0,0 +1,164 @@ +package tbSame1.tbSame1jniclient; + +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; + +import tbSame1.tbSame1_android_client.SameEnum2InterfaceClient; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameEnum2InterfaceJniClient extends AbstractSameEnum2Interface implements ISameEnum2InterfaceEventListener +{ + + private static final String TAG = "SameEnum2InterfaceJniClient"; + + private SameEnum2InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame1.tbSame1jniservice.SameEnum2InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Enum1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public Enum2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + public Enum1 func1(Enum1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Enum1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Enum1 param1) + public CompletableFuture func1Async(Enum1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public Enum1 func2(Enum1 param1, Enum2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, Enum1 param1, Enum2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, Enum1 param1, Enum2 param2) + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum2InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + private native void nativeOnProp1Changed(Enum1 prop1); + private native void nativeOnProp2Changed(Enum2 prop2); + private native void nativeOnSig1(Enum1 param1); + private native void nativeOnSig2(Enum1 param1, Enum2 param2); + private native void nativeOnFunc1Result(Enum1 result, String callId); + private native void nativeOnFunc2Result(Enum1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java new file mode 100644 index 0000000..51d1c18 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java @@ -0,0 +1,118 @@ +package tbSame1.tbSame1jniclient; + +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; + +import tbSame1.tbSame1_android_client.SameStruct1InterfaceClient; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameStruct1InterfaceJniClient extends AbstractSameStruct1Interface implements ISameStruct1InterfaceEventListener +{ + + private static final String TAG = "SameStruct1InterfaceJniClient"; + + private SameStruct1InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame1.tbSame1jniservice.SameStruct1InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Struct1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + public Struct1 func1(Struct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Struct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Struct1 param1) + public CompletableFuture func1Async(Struct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct1InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + private native void nativeOnProp1Changed(Struct1 prop1); + private native void nativeOnSig1(Struct1 param1); + private native void nativeOnFunc1Result(Struct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java new file mode 100644 index 0000000..619fb09 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java @@ -0,0 +1,164 @@ +package tbSame1.tbSame1jniclient; + +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; + +import tbSame1.tbSame1_android_client.SameStruct2InterfaceClient; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameStruct2InterfaceJniClient extends AbstractSameStruct2Interface implements ISameStruct2InterfaceEventListener +{ + + private static final String TAG = "SameStruct2InterfaceJniClient"; + + private SameStruct2InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame1.tbSame1jniservice.SameStruct2InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Struct2 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public Struct2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + public Struct1 func1(Struct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Struct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Struct1 param1) + public CompletableFuture func1Async(Struct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public Struct1 func2(Struct1 param1, Struct2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, Struct1 param1, Struct2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, Struct1 param1, Struct2 param2) + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct2InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Struct2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + private native void nativeOnProp1Changed(Struct2 prop1); + private native void nativeOnProp2Changed(Struct2 prop2); + private native void nativeOnSig1(Struct1 param1); + private native void nativeOnSig2(Struct1 param1, Struct2 param2); + private native void nativeOnFunc1Result(Struct1 result, String callId); + private native void nativeOnFunc2Result(Struct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java new file mode 100644 index 0000000..14eb8eb --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java @@ -0,0 +1,95 @@ +package tbSame1.tbSame1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameEnum1InterfaceJniService extends AbstractSameEnum1Interface { + + + private final static String TAG = "SameEnum1InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameEnum1InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Enum1 prop1); + private native Enum1 nativeGetProp1(); + + // methods + private native Enum1 nativeFunc1(Enum1 param1); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java new file mode 100644 index 0000000..e15624b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1jniservice; + +import tbSame1.tbSame1_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_api.AbstractSameEnum1Interface; +import tbSame1.tbSame1jniservice.SameEnum1InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum1InterfaceJniServiceFactory thread for the system. This is a thread for + * SameEnum1InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum1InterfaceJniServiceFactory extends HandlerThread implements ISameEnum1InterfaceServiceFactory +{ + private SameEnum1InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum1InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum1Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameEnum1InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum1InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameEnum1InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameEnum1InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum1InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum1InterfaceJniServiceFactory t = new SameEnum1InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java new file mode 100644 index 0000000..e199613 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_service.SameEnum1InterfaceServiceAdapter; +import tbSame1.tbSame1jniservice.SameEnum1InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameEnum1InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum1InterfaceJniStarter"; + + + + public static ISameEnum1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum1InterfaceJniServiceFactory factory = SameEnum1InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java new file mode 100644 index 0000000..aef58eb --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java @@ -0,0 +1,137 @@ +package tbSame1.tbSame1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameEnum2InterfaceJniService extends AbstractSameEnum2Interface { + + + private final static String TAG = "SameEnum2InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameEnum2InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Enum1 func2(Enum1 param1, Enum2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Enum1 prop1); + private native Enum1 nativeGetProp1(); + + private native void nativeSetProp2(Enum2 prop2); + private native Enum2 nativeGetProp2(); + + // methods + private native Enum1 nativeFunc1(Enum1 param1); + private native Enum1 nativeFunc2(Enum1 param1, Enum2 param2); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java new file mode 100644 index 0000000..0624d94 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1jniservice; + +import tbSame1.tbSame1_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_api.AbstractSameEnum2Interface; +import tbSame1.tbSame1jniservice.SameEnum2InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum2InterfaceJniServiceFactory thread for the system. This is a thread for + * SameEnum2InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum2InterfaceJniServiceFactory extends HandlerThread implements ISameEnum2InterfaceServiceFactory +{ + private SameEnum2InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum2InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum2Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameEnum2InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum2InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameEnum2InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameEnum2InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum2InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum2InterfaceJniServiceFactory t = new SameEnum2InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java new file mode 100644 index 0000000..c7949e0 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_service.SameEnum2InterfaceServiceAdapter; +import tbSame1.tbSame1jniservice.SameEnum2InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameEnum2InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum2InterfaceJniStarter"; + + + + public static ISameEnum2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum2InterfaceJniServiceFactory factory = SameEnum2InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java new file mode 100644 index 0000000..9d70f38 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java @@ -0,0 +1,95 @@ +package tbSame1.tbSame1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameStruct1InterfaceJniService extends AbstractSameStruct1Interface { + + + private final static String TAG = "SameStruct1InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameStruct1InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Struct1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Struct1 prop1); + private native Struct1 nativeGetProp1(); + + // methods + private native Struct1 nativeFunc1(Struct1 param1); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Struct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java new file mode 100644 index 0000000..9d61054 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1jniservice; + +import tbSame1.tbSame1_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_api.AbstractSameStruct1Interface; +import tbSame1.tbSame1jniservice.SameStruct1InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct1InterfaceJniServiceFactory thread for the system. This is a thread for + * SameStruct1InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct1InterfaceJniServiceFactory extends HandlerThread implements ISameStruct1InterfaceServiceFactory +{ + private SameStruct1InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct1InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct1Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameStruct1InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct1InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameStruct1InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameStruct1InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct1InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct1InterfaceJniServiceFactory t = new SameStruct1InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java new file mode 100644 index 0000000..50ac6ec --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter; +import tbSame1.tbSame1jniservice.SameStruct1InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameStruct1InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct1InterfaceJniStarter"; + + + + public static ISameStruct1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct1InterfaceJniServiceFactory factory = SameStruct1InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java new file mode 100644 index 0000000..cfb1911 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java @@ -0,0 +1,137 @@ +package tbSame1.tbSame1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameStruct2InterfaceJniService extends AbstractSameStruct2Interface { + + + private final static String TAG = "SameStruct2InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameStruct2InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Struct2 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public Struct2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Struct1 func2(Struct1 param1, Struct2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Struct2 prop1); + private native Struct2 nativeGetProp1(); + + private native void nativeSetProp2(Struct2 prop2); + private native Struct2 nativeGetProp2(); + + // methods + private native Struct1 nativeFunc1(Struct1 param1); + private native Struct1 nativeFunc2(Struct1 param1, Struct2 param2); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Struct2 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(Struct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java new file mode 100644 index 0000000..8f8215a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame1.tbSame1jniservice; + +import tbSame1.tbSame1_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_api.AbstractSameStruct2Interface; +import tbSame1.tbSame1jniservice.SameStruct2InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct2InterfaceJniServiceFactory thread for the system. This is a thread for + * SameStruct2InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct2InterfaceJniServiceFactory extends HandlerThread implements ISameStruct2InterfaceServiceFactory +{ + private SameStruct2InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct2InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct2Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameStruct2InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct2InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameStruct2InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameStruct2InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct2InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct2InterfaceJniServiceFactory t = new SameStruct2InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java new file mode 100644 index 0000000..f1c2f2c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_service.SameStruct2InterfaceServiceAdapter; +import tbSame1.tbSame1jniservice.SameStruct2InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameStruct2InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct2InterfaceJniStarter"; + + + + public static ISameStruct2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct2InterfaceJniServiceFactory factory = SameStruct2InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle b/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle new file mode 100644 index 0000000..be898ee --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbSame1.tbSame1serviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbSame1.tbSame1serviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbSame1_api') + implementation project(':tbSame1_android_messenger') + implementation project(':tbSame1_android_service') + implementation project(':tbSame1_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b58c2d4 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java new file mode 100644 index 0000000..d65ee17 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java @@ -0,0 +1,210 @@ +package tbSame1.tbSame1serviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceAdapter; +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceFactory; +import tbSame1.tbSame1_android_service.SameStruct1InterfaceServiceStarter; + +//import message type and parcelabe types +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import java.util.concurrent.CompletableFuture; + + + +public class TbSame1TestServiceApp extends Activity implements ISameStruct1InterfaceEventListener +{ + + private static final String TAG = "TbSame1TestServiceApp"; + static Intent stub_service = null; + + + private ISameStruct1Interface mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + Struct1 newProp1 = mBackend.getProp1(); + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mBackend.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSig1 = new Button(this); + bSig1.setText("sig1"); + + bSig1.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig1 "); + Struct1 param1 = new Struct1(); + mBackend.fireSig1(param1); + }); + bSig1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig1); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, SameStruct1InterfaceServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = SameStruct1InterfaceServiceAdapter.setService(SameStruct1InterfaceServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onProp1Changed(Struct1 newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/colors.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/strings.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..37dbf8c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbSame1TestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/themes.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/gradle.properties b/goldenmaster/tbSame2/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbSame2/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbSame2/gradle/libs.versions.toml b/goldenmaster/tbSame2/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbSame2/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbSame2/settings.gradle b/goldenmaster/tbSame2/settings.gradle new file mode 100644 index 0000000..818f0a0 --- /dev/null +++ b/goldenmaster/tbSame2/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbSame2" +include ':tbSame2_android_service' +include ':tbSame2_android_client' +include ':tbSame2_android_messenger' +include ':tbSame2_impl' +include ':tbSame2_api' +include ':tbSame2_client_example' +include ':tbSame2serviceexample' \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle b/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle new file mode 100644 index 0000000..7df64c3 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'tbSame2.tbSame2_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + implementation project(':tbSame2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/build.gradle b/goldenmaster/tbSame2/tbSame2_android_client/build.gradle new file mode 100644 index 0000000..dd2a82d --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame2" +version = "1.0.0" + +android { + namespace 'tbSame2.tbSame2_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + implementation project(':tbSame2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java new file mode 100644 index 0000000..271d8b4 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java @@ -0,0 +1,339 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameEnum1InterfaceClient extends AbstractSameEnum1Interface implements ServiceConnection +{ + private static final String TAG = "SameEnum1InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Enum1 m_prop1 = Enum1.Value1; + + + public SameEnum1InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameEnum1InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameEnum1InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + onProp1(prop1); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + onProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + onSig1(param1); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameEnum1InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (m_prop1 != prop1) + { + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Enum1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + // methods + + + @Override + public Enum1 func1(Enum1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java new file mode 100644 index 0000000..728942a --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java @@ -0,0 +1,471 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameEnum2InterfaceClient extends AbstractSameEnum2Interface implements ServiceConnection +{ + private static final String TAG = "SameEnum2InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Enum1 m_prop1 = Enum1.Value1; + private Enum2 m_prop2 = Enum2.Value1; + + + public SameEnum2InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameEnum2InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameEnum2InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + onProp1(prop1); + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + onProp2(prop2); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + onProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + onSig2(param1, param2); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameEnum2InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameEnum2InterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (m_prop1 != prop1) + { + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Enum1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (m_prop2 != prop2) + { + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(Enum2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + // methods + + + @Override + public Enum1 func1(Enum1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Enum1 func2(Enum1 param1, Enum2 param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + + data.putParcelable("param2", new Enum2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum1 result = bundle.getParcelable("result", Enum1Parcelable.class).getEnum1(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java new file mode 100644 index 0000000..2e23c83 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java @@ -0,0 +1,339 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameStruct1InterfaceClient extends AbstractSameStruct1Interface implements ServiceConnection +{ + private static final String TAG = "SameStruct1InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Struct1 m_prop1 = new Struct1(); + + + public SameStruct1InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameStruct1InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameStruct1InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + + Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + onProp1(prop1); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + + Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + + onProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + onSig1(param1); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameStruct1InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Struct1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Struct1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + // methods + + + @Override + public Struct1 func1(Struct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Struct1 result = bundle.getParcelable("result", Struct1Parcelable.class).getStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java new file mode 100644 index 0000000..338193b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java @@ -0,0 +1,471 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SameStruct2InterfaceClient extends AbstractSameStruct2Interface implements ServiceConnection +{ + private static final String TAG = "SameStruct2InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private Struct2 m_prop1 = new Struct2(); + private Struct2 m_prop2 = new Struct2(); + + + public SameStruct2InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SameStruct2InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SameStruct2InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + + Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + onProp1(prop1); + + Struct2 prop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + onProp2(prop2); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + + Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + + Struct2 prop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + + onProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct2 param2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + onSig2(param1, param2); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameStruct2InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SameStruct2InterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct2Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(Struct2 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public Struct2 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (! m_prop2.equals(prop2)) + { + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Struct2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(Struct2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public Struct2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + // methods + + + @Override + public Struct1 func1(Struct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Struct1 result = bundle.getParcelable("result", Struct1Parcelable.class).getStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Struct1 func2(Struct1 param1, Struct2 param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + + data.putParcelable("param2", new Struct2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Struct1 result = bundle.getParcelable("result", Struct1Parcelable.class).getStruct1(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java new file mode 100644 index 0000000..f600e1d --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java @@ -0,0 +1,251 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame2.tbSame2_android_client; + +import tbSame2.tbSame2_android_client.SameEnum1InterfaceClient; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameEnum1InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum1InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameEnum1InterfaceClient testedClient; + private ISameEnum1InterfaceEventListener listenerMock = mock(ISameEnum1InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameEnum1InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameEnum1InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum1InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameEnum1InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameEnum1InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame2.tbSame2_android_service", "tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum1InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } + + @Test + public void setPropertyRequestprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1(testparam1); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameEnum1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java new file mode 100644 index 0000000..d3f94b0 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java @@ -0,0 +1,355 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame2.tbSame2_android_client; + +import tbSame2.tbSame2_android_client.SameEnum2InterfaceClient; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameEnum2InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum2InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameEnum2InterfaceClient testedClient; + private ISameEnum2InterfaceEventListener listenerMock = mock(ISameEnum2InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameEnum2InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameEnum2InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameEnum2InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameEnum2InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame2.tbSame2_android_service", "tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + Enum2 testprop2 = Enum2.Value2; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } + + @Test + public void setPropertyRequestprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + Enum2 testprop2 = Enum2.Value2; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + } + + @Test + public void setPropertyRequestprop2() + { + Enum2 testprop2 = Enum2.Value2; + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1(testparam1); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum2 testparam2 = Enum2.Value2; + data.putParcelable("param2", new Enum2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2(testparam1, testparam2); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + Enum1 testparam1 = Enum1.Value2; + Enum2 testparam2 = Enum2.Value2; + Enum1 expectedResult = Enum1.Value2; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameEnum2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + + Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Enum1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java new file mode 100644 index 0000000..ac02326 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java @@ -0,0 +1,251 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame2.tbSame2_android_client; + +import tbSame2.tbSame2_android_client.SameStruct1InterfaceClient; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameStruct1InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct1InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameStruct1InterfaceClient testedClient; + private ISameStruct1InterfaceEventListener listenerMock = mock(ISameStruct1InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameStruct1InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameStruct1InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct1InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameStruct1InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameStruct1InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame2.tbSame2_android_service", "tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct1InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Struct1 testprop1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("prop1", new Struct1Parcelable(testprop1)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Struct1 testprop1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("prop1", new Struct1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); + } + + @Test + public void setPropertyRequestprop1() + { + Struct1 testprop1 = TbSame2TestHelper.makeTestStruct1(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(Struct1.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + Struct1 expectedResult = TbSame2TestHelper.makeTestStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameStruct1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Struct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java new file mode 100644 index 0000000..f2705d3 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java @@ -0,0 +1,355 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSame2.tbSame2_android_client; + +import tbSame2.tbSame2_android_client.SameStruct2InterfaceClient; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISameStruct2InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct2InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SameStruct2InterfaceClient testedClient; + private ISameStruct2InterfaceEventListener listenerMock = mock(ISameStruct2InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISameStruct2InterfaceClientMessageGetter serviceMessagesStorage = mock(ISameStruct2InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISameStruct2InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SameStruct2InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSame2.tbSame2_android_service", "tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + Struct2 testprop1 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("prop1", new Struct2Parcelable(testprop1)); + Struct2 testprop2 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("prop2", new Struct2Parcelable(testprop2)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + Struct2 testprop1 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("prop1", new Struct2Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); + } + + @Test + public void setPropertyRequestprop1() + { + Struct2 testprop1 = TbSame2TestHelper.makeTestStruct2(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + Struct2 testprop2 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("prop2", new Struct2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); + } + + @Test + public void setPropertyRequestprop2() + { + Struct2 testprop2 = TbSame2TestHelper.makeTestStruct2(); + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(Struct1.class)); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct2 testparam2 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("param2", new Struct2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2( any(Struct1.class), any(Struct2.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + Struct1 expectedResult = TbSame2TestHelper.makeTestStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Struct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + Struct2 testparam2 = TbSame2TestHelper.makeTestStruct2(); + Struct1 expectedResult = TbSame2TestHelper.makeTestStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SameStruct2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + + Struct2 receivedparam2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new Struct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/additions.gradle b/goldenmaster/tbSame2/tbSame2_android_messenger/additions.gradle new file mode 100644 index 0000000..88e3d96 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbSame2.tbSame2_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/build.gradle b/goldenmaster/tbSame2/tbSame2_android_messenger/build.gradle new file mode 100644 index 0000000..564bd5c --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame2" +version = "1.0.0" + +android { + namespace 'tbSame2.tbSame2_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum1Parcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum1Parcelable.java new file mode 100644 index 0000000..aaa0427 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum1Parcelable.java @@ -0,0 +1,69 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.Enum1; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum1Parcelable implements Parcelable { + + public Enum1 data; + + public Enum1Parcelable(Enum1 data) { + this.data = data; + } + + public Enum1 getEnum1() + { + return data; + } + + protected Enum1Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum1.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum1Parcelable createFromParcel(Parcel in) { + return new Enum1Parcelable(in); + } + + @Override + public Enum1Parcelable[] newArray(int size) { + return new Enum1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum1Parcelable[] wrapArray(Enum1[] enums) { + if (enums == null) return null; + Enum1Parcelable[] result = new Enum1Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum1Parcelable(enums[i]); + } + return result; + } + + public static Enum1[] unwrapArray(Enum1Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum1[] out = new Enum1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum2Parcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum2Parcelable.java new file mode 100644 index 0000000..7429e11 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Enum2Parcelable.java @@ -0,0 +1,69 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.Enum2; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum2Parcelable implements Parcelable { + + public Enum2 data; + + public Enum2Parcelable(Enum2 data) { + this.data = data; + } + + public Enum2 getEnum2() + { + return data; + } + + protected Enum2Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum2.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum2Parcelable createFromParcel(Parcel in) { + return new Enum2Parcelable(in); + } + + @Override + public Enum2Parcelable[] newArray(int size) { + return new Enum2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum2Parcelable[] wrapArray(Enum2[] enums) { + if (enums == null) return null; + Enum2Parcelable[] result = new Enum2Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum2Parcelable(enums[i]); + } + return result; + } + + public static Enum2[] unwrapArray(Enum2Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum2[] out = new Enum2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceMessageType.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceMessageType.java new file mode 100644 index 0000000..053a4f9 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceMessageType.java @@ -0,0 +1,35 @@ +package tbSame2.tbSame2_android_messenger; + +public enum SameEnum1InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + SIG_Sig1(5), + RPC_Func1Req(6), + RPC_Func1Resp(7), + SameEnum1InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameEnum1InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameEnum1InterfaceMessageType fromInteger(int value) + { + for (SameEnum1InterfaceMessageType event : SameEnum1InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameEnum1InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceMessageType.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceMessageType.java new file mode 100644 index 0000000..67ba34f --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceMessageType.java @@ -0,0 +1,40 @@ +package tbSame2.tbSame2_android_messenger; + +public enum SameEnum2InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + SIG_Sig1(7), + SIG_Sig2(8), + RPC_Func1Req(9), + RPC_Func1Resp(10), + RPC_Func2Req(11), + RPC_Func2Resp(12), + SameEnum2InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameEnum2InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameEnum2InterfaceMessageType fromInteger(int value) + { + for (SameEnum2InterfaceMessageType event : SameEnum2InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameEnum2InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceMessageType.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceMessageType.java new file mode 100644 index 0000000..41adcf6 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceMessageType.java @@ -0,0 +1,35 @@ +package tbSame2.tbSame2_android_messenger; + +public enum SameStruct1InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + SIG_Sig1(5), + RPC_Func1Req(6), + RPC_Func1Resp(7), + SameStruct1InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameStruct1InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameStruct1InterfaceMessageType fromInteger(int value) + { + for (SameStruct1InterfaceMessageType event : SameStruct1InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameStruct1InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceMessageType.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceMessageType.java new file mode 100644 index 0000000..3f21f6a --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceMessageType.java @@ -0,0 +1,40 @@ +package tbSame2.tbSame2_android_messenger; + +public enum SameStruct2InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + SIG_Sig1(7), + SIG_Sig2(8), + RPC_Func1Req(9), + RPC_Func1Resp(10), + RPC_Func2Req(11), + RPC_Func2Resp(12), + SameStruct2InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SameStruct2InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SameStruct2InterfaceMessageType fromInteger(int value) + { + for (SameStruct2InterfaceMessageType event : SameStruct2InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SameStruct2InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct1Parcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct1Parcelable.java new file mode 100644 index 0000000..46d0abf --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct1Parcelable.java @@ -0,0 +1,69 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.Struct1; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct1Parcelable implements Parcelable { + + public Struct1 data; + + public Struct1Parcelable(Struct1 data) { + this.data = new Struct1(data); + } + + public Struct1 getStruct1() + { + return new Struct1(data); + } + + protected Struct1Parcelable(Parcel in) { + this.data = new Struct1(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + data.field3 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct1Parcelable createFromParcel(Parcel in) { + return new Struct1Parcelable(in); + } + + @Override + public Struct1Parcelable[] newArray(int size) { + return new Struct1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + dest.writeInt(data.field3); + + + } + public static Struct1Parcelable[] wrapArray(Struct1[] structs) { + if (structs == null) return null; + Struct1Parcelable[] out = new Struct1Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct1Parcelable(structs[i]); + } + return out; + } + + public static Struct1[] unwrapArray(Struct1Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct1[] out = new Struct1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct2Parcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct2Parcelable.java new file mode 100644 index 0000000..9537c7d --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/Struct2Parcelable.java @@ -0,0 +1,69 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.Struct2; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct2Parcelable implements Parcelable { + + public Struct2 data; + + public Struct2Parcelable(Struct2 data) { + this.data = new Struct2(data); + } + + public Struct2 getStruct2() + { + return new Struct2(data); + } + + protected Struct2Parcelable(Parcel in) { + this.data = new Struct2(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + data.field3 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct2Parcelable createFromParcel(Parcel in) { + return new Struct2Parcelable(in); + } + + @Override + public Struct2Parcelable[] newArray(int size) { + return new Struct2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + dest.writeInt(data.field3); + + + } + public static Struct2Parcelable[] wrapArray(Struct2[] structs) { + if (structs == null) return null; + Struct2Parcelable[] out = new Struct2Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct2Parcelable(structs[i]); + } + return out; + } + + public static Struct2[] unwrapArray(Struct2Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct2[] out = new Struct2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle b/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle new file mode 100644 index 0000000..c205d81 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbSame2.tbSame2_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + implementation project(':tbSame2_impl') + implementation project(':tbSame2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/build.gradle b/goldenmaster/tbSame2/tbSame2_android_service/build.gradle new file mode 100644 index 0000000..0d434a3 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame2" +version = "1.0.0" + + +android { + namespace 'tbSame2.tbSame2_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + implementation project(':tbSame2_impl') + implementation project(':tbSame2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbSame2/tbSame2_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c13aeed --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum1InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum1InterfaceServiceFactory.java new file mode 100644 index 0000000..3f54c5a --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum1InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame2.tbSame2_android_service; +import tbSame2.tbSame2_api.ISameEnum1Interface; + + +public interface ISameEnum1InterfaceServiceFactory { + public ISameEnum1Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum2InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum2InterfaceServiceFactory.java new file mode 100644 index 0000000..2595e9d --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameEnum2InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame2.tbSame2_android_service; +import tbSame2.tbSame2_api.ISameEnum2Interface; + + +public interface ISameEnum2InterfaceServiceFactory { + public ISameEnum2Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct1InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct1InterfaceServiceFactory.java new file mode 100644 index 0000000..634ac82 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct1InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame2.tbSame2_android_service; +import tbSame2.tbSame2_api.ISameStruct1Interface; + + +public interface ISameStruct1InterfaceServiceFactory { + public ISameStruct1Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct2InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct2InterfaceServiceFactory.java new file mode 100644 index 0000000..e1d61ad --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/ISameStruct2InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSame2.tbSame2_android_service; +import tbSame2.tbSame2_api.ISameStruct2Interface; + + +public interface ISameStruct2InterfaceServiceFactory { + public ISameStruct2Interface getServiceInstance(); +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java new file mode 100644 index 0000000..304e398 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java @@ -0,0 +1,300 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameEnum1InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameEnum1InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameEnum1Interface mBackendService; + private static ISameEnum1InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameEnum1InterfaceServiceAdapter() + { + } + + public static ISameEnum1Interface setService(ISameEnum1InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum1InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameEnum1InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameEnum1InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameEnum1InterfaceMessageType.fromInteger(msg.what) != SameEnum1InterfaceMessageType.REGISTER_CLIENT + && SameEnum1InterfaceMessageType.fromInteger(msg.what) != SameEnum1InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameEnum1InterfaceMessageType" + SameEnum1InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameEnum1InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + mBackendService.setProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameEnum1InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Enum1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Enum1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Enum1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum1InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java new file mode 100644 index 0000000..70e89ed --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import tbSame2.tbSame2_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum1InterfaceServiceFactory thread for the system. This is a thread for + * SameEnum1InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum1InterfaceServiceFactory extends HandlerThread implements ISameEnum1InterfaceServiceFactory +{ + private SameEnum1InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum1InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum1Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameEnum1InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum1InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameEnum1InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameEnum1InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum1InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum1InterfaceServiceFactory t = new SameEnum1InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java new file mode 100644 index 0000000..a1a1f96 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter; +import tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame2.tbSame2_impl; . +public class SameEnum1InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum1InterfaceStarter"; + + + + public static ISameEnum1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum1InterfaceServiceFactory factory = SameEnum1InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java new file mode 100644 index 0000000..6e0c24f --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java @@ -0,0 +1,369 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameEnum2InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameEnum2InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameEnum2Interface mBackendService; + private static ISameEnum2InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameEnum2InterfaceServiceAdapter() + { + } + + public static ISameEnum2Interface setService(ISameEnum2InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum2InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameEnum2InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameEnum2InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameEnum2InterfaceMessageType.fromInteger(msg.what) != SameEnum2InterfaceMessageType.REGISTER_CLIENT + && SameEnum2InterfaceMessageType.fromInteger(msg.what) != SameEnum2InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameEnum2InterfaceMessageType" + SameEnum2InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameEnum2InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum2 prop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + mBackendService.setProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameEnum2InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + + Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + + Enum1 result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = SameEnum2InterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Enum1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Enum1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + Enum2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Enum1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Enum1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(Enum2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Enum2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Enum1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(Enum1 param1, Enum2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameEnum2InterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Enum1Parcelable(param1)); + + data.putParcelable("param2", new Enum2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java new file mode 100644 index 0000000..4cfbe3e --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import tbSame2.tbSame2_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum2InterfaceServiceFactory thread for the system. This is a thread for + * SameEnum2InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum2InterfaceServiceFactory extends HandlerThread implements ISameEnum2InterfaceServiceFactory +{ + private SameEnum2InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum2InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum2Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameEnum2InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum2InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameEnum2InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameEnum2InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum2InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum2InterfaceServiceFactory t = new SameEnum2InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java new file mode 100644 index 0000000..a07aefe --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter; +import tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame2.tbSame2_impl; . +public class SameEnum2InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum2InterfaceStarter"; + + + + public static ISameEnum2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum2InterfaceServiceFactory factory = SameEnum2InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java new file mode 100644 index 0000000..03cf9b3 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java @@ -0,0 +1,300 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameStruct1InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameStruct1InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameStruct1Interface mBackendService; + private static ISameStruct1InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameStruct1InterfaceServiceAdapter() + { + } + + public static ISameStruct1Interface setService(ISameStruct1InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct1InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameStruct1InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameStruct1InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameStruct1InterfaceMessageType.fromInteger(msg.what) != SameStruct1InterfaceMessageType.REGISTER_CLIENT + && SameStruct1InterfaceMessageType.fromInteger(msg.what) != SameStruct1InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameStruct1InterfaceMessageType" + SameStruct1InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameStruct1InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + mBackendService.setProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameStruct1InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Struct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Struct1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Struct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Struct1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Struct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct1InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java new file mode 100644 index 0000000..0e1b16b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import tbSame2.tbSame2_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct1InterfaceServiceFactory thread for the system. This is a thread for + * SameStruct1InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct1InterfaceServiceFactory extends HandlerThread implements ISameStruct1InterfaceServiceFactory +{ + private SameStruct1InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct1InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct1Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameStruct1InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct1InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameStruct1InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameStruct1InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct1InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct1InterfaceServiceFactory t = new SameStruct1InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java new file mode 100644 index 0000000..3f277cc --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter; +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame2.tbSame2_impl; . +public class SameStruct1InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct1InterfaceStarter"; + + + + public static ISameStruct1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct1InterfaceServiceFactory factory = SameStruct1InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java new file mode 100644 index 0000000..cc6f4f8 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java @@ -0,0 +1,369 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SameStruct2InterfaceServiceAdapter extends Service +{ + private static final String TAG = "SameStruct2InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISameStruct2Interface mBackendService; + private static ISameStruct2InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SameStruct2InterfaceServiceAdapter() + { + } + + public static ISameStruct2Interface setService(ISameStruct2InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct2InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SameStruct2InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISameStruct2InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SameStruct2InterfaceMessageType.fromInteger(msg.what) != SameStruct2InterfaceMessageType.REGISTER_CLIENT + && SameStruct2InterfaceMessageType.fromInteger(msg.what) != SameStruct2InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SameStruct2InterfaceMessageType" + SameStruct2InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SameStruct2InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct2 prop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + mBackendService.setProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = SameStruct2InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Struct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + + Struct2 param2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + + Struct1 result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = SameStruct2InterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new Struct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + Struct2 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new Struct2Parcelable(prop1)); + Struct2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new Struct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(Struct2 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new Struct2Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(Struct2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new Struct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(Struct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(Struct1 param1, Struct2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = SameStruct2InterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new Struct1Parcelable(param1)); + + data.putParcelable("param2", new Struct2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java new file mode 100644 index 0000000..fd015cb --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import tbSame2.tbSame2_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct2InterfaceServiceFactory thread for the system. This is a thread for + * SameStruct2InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct2InterfaceServiceFactory extends HandlerThread implements ISameStruct2InterfaceServiceFactory +{ + private SameStruct2InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct2InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct2Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SameStruct2InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct2InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SameStruct2InterfaceServiceFactory INSTANCE = createInstance(); + } + + private SameStruct2InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct2InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct2InterfaceServiceFactory t = new SameStruct2InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java new file mode 100644 index 0000000..63df222 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter; +import tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSame2.tbSame2_impl; . +public class SameStruct2InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct2InterfaceStarter"; + + + + public static ISameStruct2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct2InterfaceServiceFactory factory = SameStruct2InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..07818a3 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -0,0 +1,295 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameEnum1InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum1InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameEnum1InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameEnum1InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameEnum1Interface backendServiceMock = mock(ISameEnum1Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameEnum1InterfaceServiceFactory serviceFactory = mock(ISameEnum1InterfaceServiceFactory.class); + private ISameEnum1InterfaceMessageGetter clientMessagesStorage = mock(ISameEnum1InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameEnum1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameEnum1InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Enum1 initprop1 = Enum1.Value2; + when(backendServiceMock.getProp1()).thenReturn(initprop1); + + + Message registerMsg = Message.obtain(null, SameEnum1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + assertEquals(receivedprop1, initprop1); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameEnum1InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameEnum1InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameEnum1InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1(testprop1); + + } + + @Test + public void whenNotifiedprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() + { + Enum1 testparam1 = Enum1.Value2; + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum1InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func1(testparam1)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1(testparam1); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum1InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..04b6b30 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -0,0 +1,396 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameEnum2InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameEnum2InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameEnum2InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameEnum2InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameEnum2Interface backendServiceMock = mock(ISameEnum2Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameEnum2InterfaceServiceFactory serviceFactory = mock(ISameEnum2InterfaceServiceFactory.class); + private ISameEnum2InterfaceMessageGetter clientMessagesStorage = mock(ISameEnum2InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameEnum2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameEnum2InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Enum1 initprop1 = Enum1.Value2; + when(backendServiceMock.getProp1()).thenReturn(initprop1); + Enum2 initprop2 = Enum2.Value2; + when(backendServiceMock.getProp2()).thenReturn(initprop2); + + + Message registerMsg = Message.obtain(null, SameEnum2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + assertEquals(receivedprop1, initprop1); + assertEquals(receivedprop2, initprop2); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameEnum2InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameEnum2InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameEnum2InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Enum1 testprop1 = Enum1.Value2; + data.putParcelable("prop1", new Enum1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1(testprop1); + + } + + @Test + public void whenNotifiedprop1() + { + Enum1 testprop1 = Enum1.Value2; + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + Enum2 testprop2 = Enum2.Value2; + data.putParcelable("prop2", new Enum2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2(testprop2); + + } + + @Test + public void whenNotifiedprop2() + { + Enum2 testprop2 = Enum2.Value2; + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); + + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() + { + Enum1 testparam1 = Enum1.Value2; + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + Enum1 testparam1 = Enum1.Value2; + Enum2 testparam2 = Enum2.Value2; + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); + assertEquals(receivedparam1, testparam1); + + Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); + assertEquals(receivedparam2, testparam2); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func1(testparam1)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1(testparam1); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameEnum2InterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum1 testparam1 = Enum1.Value2; + data.putParcelable("param1", new Enum1Parcelable(testparam1)); + Enum2 testparam2 = Enum2.Value2; + data.putParcelable("param2", new Enum2Parcelable(testparam2)); + Enum1 returnedValue = Enum1.Value2; + + + when(backendServiceMock.func2(testparam1, testparam2)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2(testparam1, testparam2); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameEnum2InterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + Enum1 receivedByClient = resp_data.getParcelable("result", Enum1Parcelable.class).getEnum1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..1e66a5f --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -0,0 +1,296 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameStruct1InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct1InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameStruct1InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameStruct1InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameStruct1Interface backendServiceMock = mock(ISameStruct1Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameStruct1InterfaceServiceFactory serviceFactory = mock(ISameStruct1InterfaceServiceFactory.class); + private ISameStruct1InterfaceMessageGetter clientMessagesStorage = mock(ISameStruct1InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameStruct1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameStruct1InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Struct1 initprop1 = new Struct1(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + + + Message registerMsg = Message.obtain(null, SameStruct1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameStruct1InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameStruct1InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameStruct1InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Struct1 testprop1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("prop1", new Struct1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(Struct1.class)); + + } + + @Test + public void whenNotifiedprop1() + { + Struct1 testprop1 = TbSame2TestHelper.makeTestStruct1(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() + { + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct1InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct1 returnedValue = TbSame2TestHelper.makeTestStruct1(); + + + when(backendServiceMock.func1( any(Struct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(Struct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct1InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + Struct1 receivedByClient = resp_data.getParcelable("result", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..f824a2c --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -0,0 +1,398 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.TbSame2TestHelper; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISameStruct2InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SameStruct2InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SameStruct2InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISameStruct2InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISameStruct2Interface backendServiceMock = mock(ISameStruct2Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISameStruct2InterfaceServiceFactory serviceFactory = mock(ISameStruct2InterfaceServiceFactory.class); + private ISameStruct2InterfaceMessageGetter clientMessagesStorage = mock(ISameStruct2InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SameStruct2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISameStruct2InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + Struct2 initprop1 = new Struct2(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + Struct2 initprop2 = new Struct2(); + //TODO fill fields + when(backendServiceMock.getProp2()).thenReturn(initprop2); + + + Message registerMsg = Message.obtain(null, SameStruct2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + + Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + // assertEquals(receivedprop2, initprop2); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SameStruct2InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SameStruct2InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISameStruct2InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + Struct2 testprop1 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("prop1", new Struct2Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(Struct2.class)); + + } + + @Test + public void whenNotifiedprop1() + { + Struct2 testprop1 = TbSame2TestHelper.makeTestStruct2(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + Struct2 testprop2 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("prop2", new Struct2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2( any(Struct2.class)); + + } + + @Test + public void whenNotifiedprop2() + { + Struct2 testprop2 = TbSame2TestHelper.makeTestStruct2(); + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); + + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() + { + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + Struct2 testparam2 = TbSame2TestHelper.makeTestStruct2(); + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); + assertEquals(receivedparam1, testparam1); + + Struct2 receivedparam2 = data.getParcelable("param2", Struct2Parcelable.class).getStruct2(); + assertEquals(receivedparam2, testparam2); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct1 returnedValue = TbSame2TestHelper.makeTestStruct1(); + + + when(backendServiceMock.func1( any(Struct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(Struct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + Struct1 receivedByClient = resp_data.getParcelable("result", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SameStruct2InterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Struct1 testparam1 = TbSame2TestHelper.makeTestStruct1(); + data.putParcelable("param1", new Struct1Parcelable(testparam1)); + Struct2 testparam2 = TbSame2TestHelper.makeTestStruct2(); + data.putParcelable("param2", new Struct2Parcelable(testparam2)); + Struct1 returnedValue = TbSame2TestHelper.makeTestStruct1(); + + + when(backendServiceMock.func2( any(Struct1.class), any(Struct2.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2( any(Struct1.class), any(Struct2.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SameStruct2InterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + Struct1 receivedByClient = resp_data.getParcelable("result", Struct1Parcelable.class).getStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_api/additions.gradle b/goldenmaster/tbSame2/tbSame2_api/additions.gradle new file mode 100644 index 0000000..b0de283 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'tbSame2.tbSame2_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_api/build.gradle b/goldenmaster/tbSame2/tbSame2_api/build.gradle new file mode 100644 index 0000000..caa236c --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "tbSame2" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java new file mode 100644 index 0000000..9c3a0b6 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java @@ -0,0 +1,43 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum1Interface; +//TODO imported/extern modules +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameEnum1Interface implements ISameEnum1Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameEnum1InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameEnum1InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Enum1 newValue) { + for (ISameEnum1InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireSig1(Enum1 param1) { + for (ISameEnum1InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameEnum1InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java new file mode 100644 index 0000000..f1b6159 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java @@ -0,0 +1,57 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum2Interface; +//TODO imported/extern modules +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameEnum2Interface implements ISameEnum2Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameEnum2InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameEnum2InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Enum1 newValue) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(Enum2 newValue) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireSig1(Enum1 param1) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(Enum1 param1, Enum2 param2) { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameEnum2InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java new file mode 100644 index 0000000..600480e --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java @@ -0,0 +1,43 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +//TODO imported/extern modules +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameStruct1Interface implements ISameStruct1Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameStruct1InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameStruct1InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Struct1 newValue) { + for (ISameStruct1InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireSig1(Struct1 param1) { + for (ISameStruct1InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameStruct1InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java new file mode 100644 index 0000000..43f5234 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java @@ -0,0 +1,57 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct2Interface; +//TODO imported/extern modules +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSameStruct2Interface implements ISameStruct2Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISameStruct2InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISameStruct2InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(Struct2 newValue) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(Struct2 newValue) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireSig1(Struct1 param1) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(Struct1 param1, Struct2 param2) { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISameStruct2InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum1.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum1.java new file mode 100644 index 0000000..9a4f047 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum1.java @@ -0,0 +1,28 @@ +package tbSame2.tbSame2_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum1 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2); + + private final int value; + + Enum1(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum1 fromValue(int value) { + for (Enum1 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum2.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum2.java new file mode 100644 index 0000000..ef20c35 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Enum2.java @@ -0,0 +1,28 @@ +package tbSame2.tbSame2_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum2 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2); + + private final int value; + + Enum2(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum2 fromValue(int value) { + for (Enum2 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1Interface.java new file mode 100644 index 0000000..cfde155 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1Interface.java @@ -0,0 +1,27 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameEnum1Interface { + // properties + void setProp1(Enum1 prop1); + Enum1 getProp1(); + void fireProp1Changed(Enum1 newValue); + + // methods + Enum1 func1(Enum1 param1); + CompletableFuture func1Async(Enum1 param1); + public void fireSig1(Enum1 param1); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameEnum1InterfaceEventListener listener); + void removeEventListener(ISameEnum1InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1InterfaceEventListener.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1InterfaceEventListener.java new file mode 100644 index 0000000..a58b87b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum1InterfaceEventListener.java @@ -0,0 +1,11 @@ +package tbSame2.tbSame2_api; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + public interface ISameEnum1InterfaceEventListener { + void onProp1Changed(Enum1 newValue); + void onSig1(Enum1 param1); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2Interface.java new file mode 100644 index 0000000..811db84 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2Interface.java @@ -0,0 +1,34 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameEnum2Interface { + // properties + void setProp1(Enum1 prop1); + Enum1 getProp1(); + void fireProp1Changed(Enum1 newValue); + + void setProp2(Enum2 prop2); + Enum2 getProp2(); + void fireProp2Changed(Enum2 newValue); + + // methods + Enum1 func1(Enum1 param1); + CompletableFuture func1Async(Enum1 param1); + Enum1 func2(Enum1 param1, Enum2 param2); + CompletableFuture func2Async(Enum1 param1, Enum2 param2); + public void fireSig1(Enum1 param1); + public void fireSig2(Enum1 param1, Enum2 param2); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameEnum2InterfaceEventListener listener); + void removeEventListener(ISameEnum2InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2InterfaceEventListener.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2InterfaceEventListener.java new file mode 100644 index 0000000..5593543 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameEnum2InterfaceEventListener.java @@ -0,0 +1,13 @@ +package tbSame2.tbSame2_api; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + public interface ISameEnum2InterfaceEventListener { + void onProp1Changed(Enum1 newValue); + void onProp2Changed(Enum2 newValue); + void onSig1(Enum1 param1); + void onSig2(Enum1 param1, Enum2 param2); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1Interface.java new file mode 100644 index 0000000..c506117 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1Interface.java @@ -0,0 +1,27 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameStruct1Interface { + // properties + void setProp1(Struct1 prop1); + Struct1 getProp1(); + void fireProp1Changed(Struct1 newValue); + + // methods + Struct1 func1(Struct1 param1); + CompletableFuture func1Async(Struct1 param1); + public void fireSig1(Struct1 param1); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameStruct1InterfaceEventListener listener); + void removeEventListener(ISameStruct1InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1InterfaceEventListener.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1InterfaceEventListener.java new file mode 100644 index 0000000..91d8778 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct1InterfaceEventListener.java @@ -0,0 +1,11 @@ +package tbSame2.tbSame2_api; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + public interface ISameStruct1InterfaceEventListener { + void onProp1Changed(Struct1 newValue); + void onSig1(Struct1 param1); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2Interface.java new file mode 100644 index 0000000..ed8c671 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2Interface.java @@ -0,0 +1,34 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + +import java.util.concurrent.CompletableFuture; + + + public interface ISameStruct2Interface { + // properties + void setProp1(Struct2 prop1); + Struct2 getProp1(); + void fireProp1Changed(Struct2 newValue); + + void setProp2(Struct2 prop2); + Struct2 getProp2(); + void fireProp2Changed(Struct2 newValue); + + // methods + Struct1 func1(Struct1 param1); + CompletableFuture func1Async(Struct1 param1); + Struct1 func2(Struct1 param1, Struct2 param2); + CompletableFuture func2Async(Struct1 param1, Struct2 param2); + public void fireSig1(Struct1 param1); + public void fireSig2(Struct1 param1, Struct2 param2); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISameStruct2InterfaceEventListener listener); + void removeEventListener(ISameStruct2InterfaceEventListener listener); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2InterfaceEventListener.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2InterfaceEventListener.java new file mode 100644 index 0000000..b86d36e --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/ISameStruct2InterfaceEventListener.java @@ -0,0 +1,13 @@ +package tbSame2.tbSame2_api; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + public interface ISameStruct2InterfaceEventListener { + void onProp1Changed(Struct2 newValue); + void onProp2Changed(Struct2 newValue); + void onSig1(Struct1 param1); + void onSig2(Struct1 param1, Struct2 param2); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct1.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct1.java new file mode 100644 index 0000000..8e810fd --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct1.java @@ -0,0 +1,55 @@ +package tbSame2.tbSame2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct1 { + + public Struct1(int field1, int field2, int field3) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public Struct1() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + @JsonProperty("field3") + public int field3; + + public Struct1(Struct1 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + this.field3 = other.field3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct1)) return false; + Struct1 other = (Struct1) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2 + && this.field3 == other.field3; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + result = 31 * result + Integer.hashCode(field3); + return result; + } + + +} diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct2.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct2.java new file mode 100644 index 0000000..33c5083 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/Struct2.java @@ -0,0 +1,55 @@ +package tbSame2.tbSame2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct2 { + + public Struct2(int field1, int field2, int field3) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public Struct2() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + @JsonProperty("field3") + public int field3; + + public Struct2(Struct2 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + this.field3 = other.field3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct2)) return false; + Struct2 other = (Struct2) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2 + && this.field3 == other.field3; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + result = 31 * result + Integer.hashCode(field3); + return result; + } + + +} diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java new file mode 100644 index 0000000..220726c --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java @@ -0,0 +1,29 @@ +package tbSame2.tbSame2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class TbSame2TestHelper +{ + + static public Struct1 makeTestStruct1() + { + Struct1 testStruct = new Struct1(); + testStruct.field1 = 1; + testStruct.field2 = 1; + testStruct.field3 = 1; + return testStruct; + } + + static public Struct2 makeTestStruct2() + { + Struct2 testStruct = new Struct2(); + testStruct.field1 = 1; + testStruct.field2 = 1; + testStruct.field3 = 1; + return testStruct; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/build.gradle b/goldenmaster/tbSame2/tbSame2_client_example/build.gradle new file mode 100644 index 0000000..a80198d --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbSame2.tbSame2_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbSame2.tbSame2_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbSame2_api') + implementation project(':tbSame2_android_messenger') + implementation project(':tbSame2_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a9d39ec --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java new file mode 100644 index 0000000..77f441b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java @@ -0,0 +1,237 @@ +package tbSame2.tbSame2_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbSame2.tbSame2_android_client.SameStruct1InterfaceClient; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbSame2TestClientApp extends Activity implements ISameStruct1InterfaceEventListener +{ + + private static final String TAG = "TbSame2TestClientApp"; + + private SameStruct1InterfaceClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbSame2.tbSame2serviceexample.TbSame2TestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + Struct1 newProp1 = new Struct1(mClient.getProp1();); + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mClient.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bFunc1 = new Button(this); + bFunc1.setText("func1"); + + bFunc1.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func1 "); + Struct1 param1 = new Struct1(); + CompletableFuture method_res + = mClient.func1Async(param1).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func1 result "+ i); + return i; + }); + }); + bFunc1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc1); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new SameStruct1InterfaceClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onProp1Changed(Struct1 newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/colors.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/strings.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..f45e3bb --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbSame2TestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/themes.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_impl/additions.gradle b/goldenmaster/tbSame2/tbSame2_impl/additions.gradle new file mode 100644 index 0000000..e9b2135 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbSame2.tbSame2_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_impl/build.gradle b/goldenmaster/tbSame2/tbSame2_impl/build.gradle new file mode 100644 index 0000000..61c1202 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSame2" +version = "1.0.0" + +android { + namespace 'tbSame2.tbSame2_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSame2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java new file mode 100644 index 0000000..1977094 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java @@ -0,0 +1,89 @@ +package tbSame2.tbSame2_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameEnum1InterfaceService extends AbstractSameEnum1Interface { + + private final static String TAG = "SameEnum1InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Enum1 m_prop1 = Enum1.Value1; + + public SameEnum1InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java new file mode 100644 index 0000000..e8be9f4 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java @@ -0,0 +1,133 @@ +package tbSame2.tbSame2_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameEnum2InterfaceService extends AbstractSameEnum2Interface { + + private final static String TAG = "SameEnum2InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Enum1 m_prop1 = Enum1.Value1; + private Enum2 m_prop2 = Enum2.Value1; + + public SameEnum2InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Enum1 func2(Enum1 param1, Enum2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return Enum1.Value1; + } + + @Override + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java new file mode 100644 index 0000000..42385d6 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java @@ -0,0 +1,89 @@ +package tbSame2.tbSame2_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameStruct1InterfaceService extends AbstractSameStruct1Interface { + + private final static String TAG = "SameStruct1InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Struct1 m_prop1 = new Struct1(); + + public SameStruct1InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Struct1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new Struct1(); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Struct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java new file mode 100644 index 0000000..dc905c7 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java @@ -0,0 +1,133 @@ +package tbSame2.tbSame2_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SameStruct2InterfaceService extends AbstractSameStruct2Interface { + + private final static String TAG = "SameStruct2InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private Struct2 m_prop1 = new Struct2(); + private Struct2 m_prop2 = new Struct2(); + + public SameStruct2InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public Struct2 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public Struct2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new Struct1(); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Struct1 func2(Struct1 param1, Struct2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return new Struct1(); + } + + @Override + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(Struct2 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(Struct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java new file mode 100644 index 0000000..bf75c2b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java @@ -0,0 +1,118 @@ +package tbSame2.tbSame2jniclient; + +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; + +import tbSame2.tbSame2_android_client.SameEnum1InterfaceClient; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameEnum1InterfaceJniClient extends AbstractSameEnum1Interface implements ISameEnum1InterfaceEventListener +{ + + private static final String TAG = "SameEnum1InterfaceJniClient"; + + private SameEnum1InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame2.tbSame2jniservice.SameEnum1InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Enum1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + public Enum1 func1(Enum1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Enum1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Enum1 param1) + public CompletableFuture func1Async(Enum1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum1InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + private native void nativeOnProp1Changed(Enum1 prop1); + private native void nativeOnSig1(Enum1 param1); + private native void nativeOnFunc1Result(Enum1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java new file mode 100644 index 0000000..ddf8c02 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java @@ -0,0 +1,164 @@ +package tbSame2.tbSame2jniclient; + +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; + +import tbSame2.tbSame2_android_client.SameEnum2InterfaceClient; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameEnum2InterfaceJniClient extends AbstractSameEnum2Interface implements ISameEnum2InterfaceEventListener +{ + + private static final String TAG = "SameEnum2InterfaceJniClient"; + + private SameEnum2InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame2.tbSame2jniservice.SameEnum2InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Enum1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public Enum2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + public Enum1 func1(Enum1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Enum1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Enum1 param1) + public CompletableFuture func1Async(Enum1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public Enum1 func2(Enum1 param1, Enum2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, Enum1 param1, Enum2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, Enum1 param1, Enum2 param2) + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum2InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + private native void nativeOnProp1Changed(Enum1 prop1); + private native void nativeOnProp2Changed(Enum2 prop2); + private native void nativeOnSig1(Enum1 param1); + private native void nativeOnSig2(Enum1 param1, Enum2 param2); + private native void nativeOnFunc1Result(Enum1 result, String callId); + private native void nativeOnFunc2Result(Enum1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java new file mode 100644 index 0000000..3281be5 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java @@ -0,0 +1,118 @@ +package tbSame2.tbSame2jniclient; + +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; + +import tbSame2.tbSame2_android_client.SameStruct1InterfaceClient; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameStruct1InterfaceJniClient extends AbstractSameStruct1Interface implements ISameStruct1InterfaceEventListener +{ + + private static final String TAG = "SameStruct1InterfaceJniClient"; + + private SameStruct1InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame2.tbSame2jniservice.SameStruct1InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Struct1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + public Struct1 func1(Struct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Struct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Struct1 param1) + public CompletableFuture func1Async(Struct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct1InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + private native void nativeOnProp1Changed(Struct1 prop1); + private native void nativeOnSig1(Struct1 param1); + private native void nativeOnFunc1Result(Struct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java new file mode 100644 index 0000000..1514f16 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java @@ -0,0 +1,164 @@ +package tbSame2.tbSame2jniclient; + +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; + +import tbSame2.tbSame2_android_client.SameStruct2InterfaceClient; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SameStruct2InterfaceJniClient extends AbstractSameStruct2Interface implements ISameStruct2InterfaceEventListener +{ + + private static final String TAG = "SameStruct2InterfaceJniClient"; + + private SameStruct2InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSame2.tbSame2jniservice.SameStruct2InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public Struct2 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public Struct2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + public Struct1 func1(Struct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, Struct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, Struct1 param1) + public CompletableFuture func1Async(Struct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public Struct1 func2(Struct1 param1, Struct2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, Struct1 param1, Struct2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, Struct1 param1, Struct2 param2) + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct2InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Struct2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + private native void nativeOnProp1Changed(Struct2 prop1); + private native void nativeOnProp2Changed(Struct2 prop2); + private native void nativeOnSig1(Struct1 param1); + private native void nativeOnSig2(Struct1 param1, Struct2 param2); + private native void nativeOnFunc1Result(Struct1 result, String callId); + private native void nativeOnFunc2Result(Struct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java new file mode 100644 index 0000000..ba4d5ac --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java @@ -0,0 +1,95 @@ +package tbSame2.tbSame2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameEnum1InterfaceJniService extends AbstractSameEnum1Interface { + + + private final static String TAG = "SameEnum1InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameEnum1InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Enum1 prop1); + private native Enum1 nativeGetProp1(); + + // methods + private native Enum1 nativeFunc1(Enum1 param1); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java new file mode 100644 index 0000000..c394d20 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2jniservice; + +import tbSame2.tbSame2_android_service.ISameEnum1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_api.AbstractSameEnum1Interface; +import tbSame2.tbSame2jniservice.SameEnum1InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum1InterfaceJniServiceFactory thread for the system. This is a thread for + * SameEnum1InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum1InterfaceJniServiceFactory extends HandlerThread implements ISameEnum1InterfaceServiceFactory +{ + private SameEnum1InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum1InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum1Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameEnum1InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum1InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameEnum1InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameEnum1InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum1InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum1InterfaceJniServiceFactory t = new SameEnum1InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java new file mode 100644 index 0000000..3f78ed4 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_service.SameEnum1InterfaceServiceAdapter; +import tbSame2.tbSame2jniservice.SameEnum1InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameEnum1InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum1InterfaceJniStarter"; + + + + public static ISameEnum1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum1InterfaceJniServiceFactory factory = SameEnum1InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java new file mode 100644 index 0000000..d1b0216 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java @@ -0,0 +1,137 @@ +package tbSame2.tbSame2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameEnum2InterfaceJniService extends AbstractSameEnum2Interface { + + + private final static String TAG = "SameEnum2InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameEnum2InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Enum1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Enum1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(Enum2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public Enum2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + // methods + + @Override + public Enum1 func1(Enum1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Enum1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Enum1 func2(Enum1 param1, Enum2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(Enum1 param1, Enum2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Enum1 prop1); + private native Enum1 nativeGetProp1(); + + private native void nativeSetProp2(Enum2 prop2); + private native Enum2 nativeGetProp2(); + + // methods + private native Enum1 nativeFunc1(Enum1 param1); + private native Enum1 nativeFunc2(Enum1 param1, Enum2 param2); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Enum1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java new file mode 100644 index 0000000..f056ba1 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2jniservice; + +import tbSame2.tbSame2_android_service.ISameEnum2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_api.AbstractSameEnum2Interface; +import tbSame2.tbSame2jniservice.SameEnum2InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameEnum2InterfaceJniServiceFactory thread for the system. This is a thread for + * SameEnum2InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameEnum2InterfaceJniServiceFactory extends HandlerThread implements ISameEnum2InterfaceServiceFactory +{ + private SameEnum2InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameEnum2InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameEnum2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameEnum2Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameEnum2InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameEnum2InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameEnum2InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameEnum2InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameEnum2InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameEnum2InterfaceJniServiceFactory t = new SameEnum2InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java new file mode 100644 index 0000000..3d13945 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_service.SameEnum2InterfaceServiceAdapter; +import tbSame2.tbSame2jniservice.SameEnum2InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameEnum2InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameEnum2InterfaceJniStarter"; + + + + public static ISameEnum2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameEnum2InterfaceJniServiceFactory factory = SameEnum2InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java new file mode 100644 index 0000000..2fdb3fb --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java @@ -0,0 +1,95 @@ +package tbSame2.tbSame2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameStruct1InterfaceJniService extends AbstractSameStruct1Interface { + + + private final static String TAG = "SameStruct1InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameStruct1InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Struct1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Struct1 prop1); + private native Struct1 nativeGetProp1(); + + // methods + private native Struct1 nativeFunc1(Struct1 param1); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Struct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java new file mode 100644 index 0000000..15dd505 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2jniservice; + +import tbSame2.tbSame2_android_service.ISameStruct1InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_api.AbstractSameStruct1Interface; +import tbSame2.tbSame2jniservice.SameStruct1InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct1InterfaceJniServiceFactory thread for the system. This is a thread for + * SameStruct1InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct1InterfaceJniServiceFactory extends HandlerThread implements ISameStruct1InterfaceServiceFactory +{ + private SameStruct1InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct1InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct1Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameStruct1InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct1InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameStruct1InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameStruct1InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct1InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct1InterfaceJniServiceFactory t = new SameStruct1InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java new file mode 100644 index 0000000..22f3d38 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter; +import tbSame2.tbSame2jniservice.SameStruct1InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameStruct1InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct1InterfaceJniStarter"; + + + + public static ISameStruct1Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct1InterfaceJniServiceFactory factory = SameStruct1InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java new file mode 100644 index 0000000..0602e12 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java @@ -0,0 +1,137 @@ +package tbSame2.tbSame2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SameStruct2InterfaceJniService extends AbstractSameStruct2Interface { + + + private final static String TAG = "SameStruct2InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SameStruct2InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(Struct2 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public Struct2 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(Struct2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public Struct2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + // methods + + @Override + public Struct1 func1(Struct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(Struct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public Struct1 func2(Struct1 param1, Struct2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(Struct1 param1, Struct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(Struct2 prop1); + private native Struct2 nativeGetProp1(); + + private native void nativeSetProp2(Struct2 prop2); + private native Struct2 nativeGetProp2(); + + // methods + private native Struct1 nativeFunc1(Struct1 param1); + private native Struct1 nativeFunc2(Struct1 param1, Struct2 param2); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(Struct2 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(Struct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(Struct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java new file mode 100644 index 0000000..66a6dcb --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSame2.tbSame2jniservice; + +import tbSame2.tbSame2_android_service.ISameStruct2InterfaceServiceFactory; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_api.AbstractSameStruct2Interface; +import tbSame2.tbSame2jniservice.SameStruct2InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SameStruct2InterfaceJniServiceFactory thread for the system. This is a thread for + * SameStruct2InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SameStruct2InterfaceJniServiceFactory extends HandlerThread implements ISameStruct2InterfaceServiceFactory +{ + private SameStruct2InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SameStruct2InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SameStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSameStruct2Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new SameStruct2InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SameStruct2InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SameStruct2InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SameStruct2InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SameStruct2InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SameStruct2InterfaceJniServiceFactory t = new SameStruct2InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java new file mode 100644 index 0000000..c96a9b8 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_service.SameStruct2InterfaceServiceAdapter; +import tbSame2.tbSame2jniservice.SameStruct2InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SameStruct2InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SameStruct2InterfaceJniStarter"; + + + + public static ISameStruct2Interface start(Context context) { + stop(context); + androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SameStruct2InterfaceJniServiceFactory factory = SameStruct2InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle b/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle new file mode 100644 index 0000000..dd8b303 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbSame2.tbSame2serviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbSame2.tbSame2serviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbSame2_api') + implementation project(':tbSame2_android_messenger') + implementation project(':tbSame2_android_service') + implementation project(':tbSame2_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3a0a9c7 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java new file mode 100644 index 0000000..3afba59 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java @@ -0,0 +1,210 @@ +package tbSame2.tbSame2serviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceAdapter; +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceFactory; +import tbSame2.tbSame2_android_service.SameStruct1InterfaceServiceStarter; + +//import message type and parcelabe types +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import java.util.concurrent.CompletableFuture; + + + +public class TbSame2TestServiceApp extends Activity implements ISameStruct1InterfaceEventListener +{ + + private static final String TAG = "TbSame2TestServiceApp"; + static Intent stub_service = null; + + + private ISameStruct1Interface mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + Struct1 newProp1 = mBackend.getProp1(); + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mBackend.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSig1 = new Button(this); + bSig1.setText("sig1"); + + bSig1.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig1 "); + Struct1 param1 = new Struct1(); + mBackend.fireSig1(param1); + }); + bSig1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig1); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, SameStruct1InterfaceServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = SameStruct1InterfaceServiceAdapter.setService(SameStruct1InterfaceServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onProp1Changed(Struct1 newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/colors.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/strings.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..ad118e2 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbSame2TestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/themes.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/gradle.properties b/goldenmaster/tbSimple/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbSimple/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbSimple/gradle/libs.versions.toml b/goldenmaster/tbSimple/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbSimple/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbSimple/settings.gradle b/goldenmaster/tbSimple/settings.gradle new file mode 100644 index 0000000..0562cf5 --- /dev/null +++ b/goldenmaster/tbSimple/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbSimple" +include ':tbSimple_android_service' +include ':tbSimple_android_client' +include ':tbSimple_android_messenger' +include ':tbSimple_impl' +include ':tbSimple_api' +include ':tbSimple_client_example' +include ':tbSimpleserviceexample' \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle b/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle new file mode 100644 index 0000000..43cf463 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'tbSimple.tbSimple_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + implementation project(':tbSimple_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/build.gradle b/goldenmaster/tbSimple/tbSimple_android_client/build.gradle new file mode 100644 index 0000000..e1c7844 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSimple" +version = "1.0.0" + +android { + namespace 'tbSimple.tbSimple_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + implementation project(':tbSimple_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java new file mode 100644 index 0000000..a5e1d80 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java @@ -0,0 +1,205 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class EmptyInterfaceClient extends AbstractEmptyInterface implements ServiceConnection +{ + private static final String TAG = "EmptyInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + + + public EmptyInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type EmptyInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, EmptyInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, EmptyInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (EmptyInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + // methods + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java new file mode 100644 index 0000000..10334cf --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java @@ -0,0 +1,329 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NoOperationsInterfaceClient extends AbstractNoOperationsInterface implements ServiceConnection +{ + private static final String TAG = "NoOperationsInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private boolean m_propBool = false; + private int m_propInt = 0; + + + public NoOperationsInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NoOperationsInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NoOperationsInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + boolean propBool = data.getBoolean("propBool", false); + onPropBool(propBool); + + int propInt = data.getInt("propInt", 0); + onPropInt(propInt); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + + + boolean propBool = data.getBoolean("propBool", false); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + + + int propInt = data.getInt("propInt", 0); + + onPropInt(propInt); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigVoid: { + + Bundle data = msg.getData(); + onSigVoid(); + break; + } + case SIG_SigBool: { + + Bundle data = msg.getData(); + + boolean paramBool = data.getBoolean("paramBool", false); + onSigBool(paramBool); + break; + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (m_propBool != propBool) + { + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("propBool", propBool); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(boolean propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (m_propBool != propBool) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (m_propInt != propInt) + { + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt", propInt); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(int propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (m_propInt != propInt) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + // methods + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigVoid() + { + Log.i(TAG, "onSigVoid received from service"); + fireSigVoid(); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java new file mode 100644 index 0000000..c4c6e58 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java @@ -0,0 +1,344 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NoPropertiesInterfaceClient extends AbstractNoPropertiesInterface implements ServiceConnection +{ + private static final String TAG = "NoPropertiesInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + + + public NoPropertiesInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NoPropertiesInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NoPropertiesInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigVoid: { + + Bundle data = msg.getData(); + onSigVoid(); + break; + } + case SIG_SigBool: { + + Bundle data = msg.getData(); + + boolean paramBool = data.getBoolean("paramBool", false); + onSigBool(paramBool); + break; + } + case RPC_FuncVoidResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NoPropertiesInterfaceMessageType.RPC_FuncVoidResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NoPropertiesInterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + // methods + + + @Override + public void funcVoid() { + CompletableFuture resFuture = funcVoidAsync(); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcVoidAsync() { + + Log.i(TAG, "Call on service funcVoid "); + Message msg = new Message(); + msg.what = NoPropertiesInterfaceMessageType.RPC_FuncVoidReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve funcVoid"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public boolean funcBool(boolean paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = NoPropertiesInterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + boolean result = bundle.getBoolean("result", false); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigVoid() + { + Log.i(TAG, "onSigVoid received from service"); + fireSigVoid(); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java new file mode 100644 index 0000000..20e58ea --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java @@ -0,0 +1,420 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NoSignalsInterfaceClient extends AbstractNoSignalsInterface implements ServiceConnection +{ + private static final String TAG = "NoSignalsInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private boolean m_propBool = false; + private int m_propInt = 0; + + + public NoSignalsInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NoSignalsInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NoSignalsInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + boolean propBool = data.getBoolean("propBool", false); + onPropBool(propBool); + + int propInt = data.getInt("propInt", 0); + onPropInt(propInt); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + + + boolean propBool = data.getBoolean("propBool", false); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + + + int propInt = data.getInt("propInt", 0); + + onPropInt(propInt); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncVoidResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NoSignalsInterfaceMessageType.RPC_FuncVoidResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NoSignalsInterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (m_propBool != propBool) + { + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("propBool", propBool); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(boolean propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (m_propBool != propBool) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (m_propInt != propInt) + { + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt", propInt); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(int propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (m_propInt != propInt) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + // methods + + + @Override + public void funcVoid() { + CompletableFuture resFuture = funcVoidAsync(); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcVoidAsync() { + + Log.i(TAG, "Call on service funcVoid "); + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.RPC_FuncVoidReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve funcVoid"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public boolean funcBool(boolean paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + boolean result = bundle.getBoolean("result", false); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java new file mode 100644 index 0000000..208b5d5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java @@ -0,0 +1,1231 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SimpleArrayInterfaceClient extends AbstractSimpleArrayInterface implements ServiceConnection +{ + private static final String TAG = "SimpleArrayInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private boolean[] m_propBool = new boolean[]{}; + private int[] m_propInt = new int[]{}; + private int[] m_propInt32 = new int[]{}; + private long[] m_propInt64 = new long[]{}; + private float[] m_propFloat = new float[]{}; + private float[] m_propFloat32 = new float[]{}; + private double[] m_propFloat64 = new double[]{}; + private String[] m_propString = new String[]{}; + private String m_propReadOnlyString = new String(); + + + public SimpleArrayInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SimpleArrayInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SimpleArrayInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + boolean[] propBool = data.getBooleanArray("propBool"); + onPropBool(propBool); + + int[] propInt = data.getIntArray("propInt"); + onPropInt(propInt); + + int[] propInt32 = data.getIntArray("propInt32"); + onPropInt32(propInt32); + + long[] propInt64 = data.getLongArray("propInt64"); + onPropInt64(propInt64); + + float[] propFloat = data.getFloatArray("propFloat"); + onPropFloat(propFloat); + + float[] propFloat32 = data.getFloatArray("propFloat32"); + onPropFloat32(propFloat32); + + double[] propFloat64 = data.getDoubleArray("propFloat64"); + onPropFloat64(propFloat64); + + String[] propString = data.getStringArray("propString"); + onPropString(propString); + + String propReadOnlyString = data.getString("propReadOnlyString", new String()); + onPropReadOnlyString(propReadOnlyString); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + + + boolean[] propBool = data.getBooleanArray("propBool"); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + + + int[] propInt = data.getIntArray("propInt"); + + onPropInt(propInt); + break; + } + case SET_PropInt32: + { + Bundle data = msg.getData(); + + + int[] propInt32 = data.getIntArray("propInt32"); + + onPropInt32(propInt32); + break; + } + case SET_PropInt64: + { + Bundle data = msg.getData(); + + + long[] propInt64 = data.getLongArray("propInt64"); + + onPropInt64(propInt64); + break; + } + case SET_PropFloat: + { + Bundle data = msg.getData(); + + + float[] propFloat = data.getFloatArray("propFloat"); + + onPropFloat(propFloat); + break; + } + case SET_PropFloat32: + { + Bundle data = msg.getData(); + + + float[] propFloat32 = data.getFloatArray("propFloat32"); + + onPropFloat32(propFloat32); + break; + } + case SET_PropFloat64: + { + Bundle data = msg.getData(); + + + double[] propFloat64 = data.getDoubleArray("propFloat64"); + + onPropFloat64(propFloat64); + break; + } + case SET_PropString: + { + Bundle data = msg.getData(); + + + String[] propString = data.getStringArray("propString"); + + onPropString(propString); + break; + } + case SET_PropReadOnlyString: + { + Bundle data = msg.getData(); + + + String propReadOnlyString = data.getString("propReadOnlyString", new String()); + + onPropReadOnlyString(propReadOnlyString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigBool: { + + Bundle data = msg.getData(); + + boolean[] paramBool = data.getBooleanArray("paramBool"); + onSigBool(paramBool); + break; + } + case SIG_SigInt: { + + Bundle data = msg.getData(); + + int[] paramInt = data.getIntArray("paramInt"); + onSigInt(paramInt); + break; + } + case SIG_SigInt32: { + + Bundle data = msg.getData(); + + int[] paramInt32 = data.getIntArray("paramInt32"); + onSigInt32(paramInt32); + break; + } + case SIG_SigInt64: { + + Bundle data = msg.getData(); + + long[] paramInt64 = data.getLongArray("paramInt64"); + onSigInt64(paramInt64); + break; + } + case SIG_SigFloat: { + + Bundle data = msg.getData(); + + float[] paramFloat = data.getFloatArray("paramFloat"); + onSigFloat(paramFloat); + break; + } + case SIG_SigFloat32: { + + Bundle data = msg.getData(); + + float[] paramFloa32 = data.getFloatArray("paramFloa32"); + onSigFloat32(paramFloa32); + break; + } + case SIG_SigFloat64: { + + Bundle data = msg.getData(); + + double[] paramFloat64 = data.getDoubleArray("paramFloat64"); + onSigFloat64(paramFloat64); + break; + } + case SIG_SigString: { + + Bundle data = msg.getData(); + + String[] paramString = data.getStringArray("paramString"); + onSigString(paramString); + break; + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncIntResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncIntResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncInt32Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncInt32Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncInt64Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncInt64Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloatResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncFloatResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloat32Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncFloat32Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloat64Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncFloat64Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncStringResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleArrayInterfaceMessageType.RPC_FuncStringResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(boolean[] propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (! Arrays.equals(m_propBool, propBool)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBooleanArray("propBool", propBool); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(boolean[] propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (! Arrays.equals(m_propBool, propBool)) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public boolean[] getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(int[] propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (! Arrays.equals(m_propInt, propInt)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putIntArray("propInt", propInt); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(int[] propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (! Arrays.equals(m_propInt, propInt)) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public int[] getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + @Override + public void setPropInt32(int[] propInt32) + { + Log.i(TAG, "request setPropInt32 called "+ propInt32); + if (! Arrays.equals(m_propInt32, propInt32)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropInt32.getValue(); + Bundle data = new Bundle(); + + data.putIntArray("propInt32", propInt32); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt32(int[] propInt32) + { + Log.i(TAG, "value received from service for PropInt32 "); + if (! Arrays.equals(m_propInt32, propInt32)) + { + m_propInt32 = propInt32; + firePropInt32Changed(propInt32); + } + + } + + @Override + public int[] getPropInt32() + { + Log.i(TAG, "request getPropInt32 called, returning local"); + return m_propInt32; + } + + + @Override + public void setPropInt64(long[] propInt64) + { + Log.i(TAG, "request setPropInt64 called "+ propInt64); + if (! Arrays.equals(m_propInt64, propInt64)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropInt64.getValue(); + Bundle data = new Bundle(); + + data.putLongArray("propInt64", propInt64); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt64(long[] propInt64) + { + Log.i(TAG, "value received from service for PropInt64 "); + if (! Arrays.equals(m_propInt64, propInt64)) + { + m_propInt64 = propInt64; + firePropInt64Changed(propInt64); + } + + } + + @Override + public long[] getPropInt64() + { + Log.i(TAG, "request getPropInt64 called, returning local"); + return m_propInt64; + } + + + @Override + public void setPropFloat(float[] propFloat) + { + Log.i(TAG, "request setPropFloat called "+ propFloat); + if (! Arrays.equals(m_propFloat, propFloat)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putFloatArray("propFloat", propFloat); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat(float[] propFloat) + { + Log.i(TAG, "value received from service for PropFloat "); + if (! Arrays.equals(m_propFloat, propFloat)) + { + m_propFloat = propFloat; + firePropFloatChanged(propFloat); + } + + } + + @Override + public float[] getPropFloat() + { + Log.i(TAG, "request getPropFloat called, returning local"); + return m_propFloat; + } + + + @Override + public void setPropFloat32(float[] propFloat32) + { + Log.i(TAG, "request setPropFloat32 called "+ propFloat32); + if (! Arrays.equals(m_propFloat32, propFloat32)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropFloat32.getValue(); + Bundle data = new Bundle(); + + data.putFloatArray("propFloat32", propFloat32); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat32(float[] propFloat32) + { + Log.i(TAG, "value received from service for PropFloat32 "); + if (! Arrays.equals(m_propFloat32, propFloat32)) + { + m_propFloat32 = propFloat32; + firePropFloat32Changed(propFloat32); + } + + } + + @Override + public float[] getPropFloat32() + { + Log.i(TAG, "request getPropFloat32 called, returning local"); + return m_propFloat32; + } + + + @Override + public void setPropFloat64(double[] propFloat64) + { + Log.i(TAG, "request setPropFloat64 called "+ propFloat64); + if (! Arrays.equals(m_propFloat64, propFloat64)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropFloat64.getValue(); + Bundle data = new Bundle(); + + data.putDoubleArray("propFloat64", propFloat64); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat64(double[] propFloat64) + { + Log.i(TAG, "value received from service for PropFloat64 "); + if (! Arrays.equals(m_propFloat64, propFloat64)) + { + m_propFloat64 = propFloat64; + firePropFloat64Changed(propFloat64); + } + + } + + @Override + public double[] getPropFloat64() + { + Log.i(TAG, "request getPropFloat64 called, returning local"); + return m_propFloat64; + } + + + @Override + public void setPropString(String[] propString) + { + Log.i(TAG, "request setPropString called "+ propString); + if (! Arrays.equals(m_propString, propString)) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropString.getValue(); + Bundle data = new Bundle(); + + data.putStringArray("propString", propString); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropString(String[] propString) + { + Log.i(TAG, "value received from service for PropString "); + if (! Arrays.equals(m_propString, propString)) + { + m_propString = propString; + firePropStringChanged(propString); + } + + } + + @Override + public String[] getPropString() + { + Log.i(TAG, "request getPropString called, returning local"); + return m_propString; + } + + + @Override + public void setPropReadOnlyString(String propReadOnlyString) + { + Log.i(TAG, "request setPropReadOnlyString called "+ propReadOnlyString); + if (m_propReadOnlyString != propReadOnlyString) + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.PROP_PropReadOnlyString.getValue(); + Bundle data = new Bundle(); + + data.putString("propReadOnlyString", propReadOnlyString); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropReadOnlyString(String propReadOnlyString) + { + Log.i(TAG, "value received from service for PropReadOnlyString "); + if (m_propReadOnlyString != propReadOnlyString) + { + m_propReadOnlyString = propReadOnlyString; + firePropReadOnlyStringChanged(propReadOnlyString); + } + + } + + @Override + public String getPropReadOnlyString() + { + Log.i(TAG, "request getPropReadOnlyString called, returning local"); + return m_propReadOnlyString; + } + + + // methods + + + @Override + public boolean[] funcBool(boolean[] paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(boolean[] paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBooleanArray("paramBool", paramBool); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + boolean[] result = bundle.getBooleanArray("result"); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int[] funcInt(int[] paramInt) { + CompletableFuture resFuture = funcIntAsync(paramInt); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcIntAsync(int[] paramInt) { + + Log.i(TAG, "Call on service funcInt "+ " " + paramInt); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncIntReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putIntArray("paramInt", paramInt); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int[] result = bundle.getIntArray("result"); + Log.v(TAG, "resolve funcInt" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int[] funcInt32(int[] paramInt32) { + CompletableFuture resFuture = funcInt32Async(paramInt32); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcInt32Async(int[] paramInt32) { + + Log.i(TAG, "Call on service funcInt32 "+ " " + paramInt32); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncInt32Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putIntArray("paramInt32", paramInt32); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int[] result = bundle.getIntArray("result"); + Log.v(TAG, "resolve funcInt32" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public long[] funcInt64(long[] paramInt64) { + CompletableFuture resFuture = funcInt64Async(paramInt64); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcInt64Async(long[] paramInt64) { + + Log.i(TAG, "Call on service funcInt64 "+ " " + paramInt64); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncInt64Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putLongArray("paramInt64", paramInt64); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + long[] result = bundle.getLongArray("result"); + Log.v(TAG, "resolve funcInt64" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public float[] funcFloat(float[] paramFloat) { + CompletableFuture resFuture = funcFloatAsync(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloatAsync(float[] paramFloat) { + + Log.i(TAG, "Call on service funcFloat "+ " " + paramFloat); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncFloatReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putFloatArray("paramFloat", paramFloat); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + float[] result = bundle.getFloatArray("result"); + Log.v(TAG, "resolve funcFloat" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public float[] funcFloat32(float[] paramFloat32) { + CompletableFuture resFuture = funcFloat32Async(paramFloat32); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloat32Async(float[] paramFloat32) { + + Log.i(TAG, "Call on service funcFloat32 "+ " " + paramFloat32); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncFloat32Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putFloatArray("paramFloat32", paramFloat32); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + float[] result = bundle.getFloatArray("result"); + Log.v(TAG, "resolve funcFloat32" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public double[] funcFloat64(double[] paramFloat) { + CompletableFuture resFuture = funcFloat64Async(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloat64Async(double[] paramFloat) { + + Log.i(TAG, "Call on service funcFloat64 "+ " " + paramFloat); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncFloat64Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putDoubleArray("paramFloat", paramFloat); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + double[] result = bundle.getDoubleArray("result"); + Log.v(TAG, "resolve funcFloat64" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public String[] funcString(String[] paramString) { + CompletableFuture resFuture = funcStringAsync(paramString); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcStringAsync(String[] paramString) { + + Log.i(TAG, "Call on service funcString "+ " " + paramString); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.RPC_FuncStringReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putStringArray("paramString", paramString); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + String[] result = bundle.getStringArray("result"); + Log.v(TAG, "resolve funcString" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigBool(boolean[] paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } + public void onSigInt(int[] paramInt) + { + Log.i(TAG, "onSigInt received from service"); + fireSigInt(paramInt); + } + public void onSigInt32(int[] paramInt32) + { + Log.i(TAG, "onSigInt32 received from service"); + fireSigInt32(paramInt32); + } + public void onSigInt64(long[] paramInt64) + { + Log.i(TAG, "onSigInt64 received from service"); + fireSigInt64(paramInt64); + } + public void onSigFloat(float[] paramFloat) + { + Log.i(TAG, "onSigFloat received from service"); + fireSigFloat(paramFloat); + } + public void onSigFloat32(float[] paramFloa32) + { + Log.i(TAG, "onSigFloat32 received from service"); + fireSigFloat32(paramFloa32); + } + public void onSigFloat64(double[] paramFloat64) + { + Log.i(TAG, "onSigFloat64 received from service"); + fireSigFloat64(paramFloat64); + } + public void onSigString(String[] paramString) + { + Log.i(TAG, "onSigString received from service"); + fireSigString(paramString); + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java new file mode 100644 index 0000000..4c3960f --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java @@ -0,0 +1,1239 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SimpleInterfaceClient extends AbstractSimpleInterface implements ServiceConnection +{ + private static final String TAG = "SimpleInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private boolean m_propBool = false; + private int m_propInt = 0; + private int m_propInt32 = 0; + private long m_propInt64 = 0L; + private float m_propFloat = 0.0f; + private float m_propFloat32 = 0.0f; + private double m_propFloat64 = 0.0; + private String m_propString = new String(); + + + public SimpleInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SimpleInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SimpleInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SimpleInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SimpleInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + boolean propBool = data.getBoolean("propBool", false); + onPropBool(propBool); + + int propInt = data.getInt("propInt", 0); + onPropInt(propInt); + + int propInt32 = data.getInt("propInt32", 0); + onPropInt32(propInt32); + + long propInt64 = data.getLong("propInt64", 0L); + onPropInt64(propInt64); + + float propFloat = data.getFloat("propFloat", 0.0f); + onPropFloat(propFloat); + + float propFloat32 = data.getFloat("propFloat32", 0.0f); + onPropFloat32(propFloat32); + + double propFloat64 = data.getDouble("propFloat64", 0.0); + onPropFloat64(propFloat64); + + String propString = data.getString("propString", new String()); + onPropString(propString); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + + + boolean propBool = data.getBoolean("propBool", false); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + + + int propInt = data.getInt("propInt", 0); + + onPropInt(propInt); + break; + } + case SET_PropInt32: + { + Bundle data = msg.getData(); + + + int propInt32 = data.getInt("propInt32", 0); + + onPropInt32(propInt32); + break; + } + case SET_PropInt64: + { + Bundle data = msg.getData(); + + + long propInt64 = data.getLong("propInt64", 0L); + + onPropInt64(propInt64); + break; + } + case SET_PropFloat: + { + Bundle data = msg.getData(); + + + float propFloat = data.getFloat("propFloat", 0.0f); + + onPropFloat(propFloat); + break; + } + case SET_PropFloat32: + { + Bundle data = msg.getData(); + + + float propFloat32 = data.getFloat("propFloat32", 0.0f); + + onPropFloat32(propFloat32); + break; + } + case SET_PropFloat64: + { + Bundle data = msg.getData(); + + + double propFloat64 = data.getDouble("propFloat64", 0.0); + + onPropFloat64(propFloat64); + break; + } + case SET_PropString: + { + Bundle data = msg.getData(); + + + String propString = data.getString("propString", new String()); + + onPropString(propString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigBool: { + + Bundle data = msg.getData(); + + boolean paramBool = data.getBoolean("paramBool", false); + onSigBool(paramBool); + break; + } + case SIG_SigInt: { + + Bundle data = msg.getData(); + + int paramInt = data.getInt("paramInt", 0); + onSigInt(paramInt); + break; + } + case SIG_SigInt32: { + + Bundle data = msg.getData(); + + int paramInt32 = data.getInt("paramInt32", 0); + onSigInt32(paramInt32); + break; + } + case SIG_SigInt64: { + + Bundle data = msg.getData(); + + long paramInt64 = data.getLong("paramInt64", 0L); + onSigInt64(paramInt64); + break; + } + case SIG_SigFloat: { + + Bundle data = msg.getData(); + + float paramFloat = data.getFloat("paramFloat", 0.0f); + onSigFloat(paramFloat); + break; + } + case SIG_SigFloat32: { + + Bundle data = msg.getData(); + + float paramFloat32 = data.getFloat("paramFloat32", 0.0f); + onSigFloat32(paramFloat32); + break; + } + case SIG_SigFloat64: { + + Bundle data = msg.getData(); + + double paramFloat64 = data.getDouble("paramFloat64", 0.0); + onSigFloat64(paramFloat64); + break; + } + case SIG_SigString: { + + Bundle data = msg.getData(); + + String paramString = data.getString("paramString", new String()); + onSigString(paramString); + break; + } + case RPC_FuncNoReturnValueResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncNoReturnValueResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncIntResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncIntResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncInt32Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncInt32Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncInt64Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncInt64Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloatResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncFloatResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloat32Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncFloat32Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloat64Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncFloat64Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncStringResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncStringResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (m_propBool != propBool) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("propBool", propBool); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(boolean propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (m_propBool != propBool) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (m_propInt != propInt) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt", propInt); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(int propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (m_propInt != propInt) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + @Override + public void setPropInt32(int propInt32) + { + Log.i(TAG, "request setPropInt32 called "+ propInt32); + if (m_propInt32 != propInt32) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropInt32.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt32", propInt32); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt32(int propInt32) + { + Log.i(TAG, "value received from service for PropInt32 "); + if (m_propInt32 != propInt32) + { + m_propInt32 = propInt32; + firePropInt32Changed(propInt32); + } + + } + + @Override + public int getPropInt32() + { + Log.i(TAG, "request getPropInt32 called, returning local"); + return m_propInt32; + } + + + @Override + public void setPropInt64(long propInt64) + { + Log.i(TAG, "request setPropInt64 called "+ propInt64); + if (m_propInt64 != propInt64) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropInt64.getValue(); + Bundle data = new Bundle(); + + data.putLong("propInt64", propInt64); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt64(long propInt64) + { + Log.i(TAG, "value received from service for PropInt64 "); + if (m_propInt64 != propInt64) + { + m_propInt64 = propInt64; + firePropInt64Changed(propInt64); + } + + } + + @Override + public long getPropInt64() + { + Log.i(TAG, "request getPropInt64 called, returning local"); + return m_propInt64; + } + + + @Override + public void setPropFloat(float propFloat) + { + Log.i(TAG, "request setPropFloat called "+ propFloat); + if (m_propFloat != propFloat) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putFloat("propFloat", propFloat); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat(float propFloat) + { + Log.i(TAG, "value received from service for PropFloat "); + if (m_propFloat != propFloat) + { + m_propFloat = propFloat; + firePropFloatChanged(propFloat); + } + + } + + @Override + public float getPropFloat() + { + Log.i(TAG, "request getPropFloat called, returning local"); + return m_propFloat; + } + + + @Override + public void setPropFloat32(float propFloat32) + { + Log.i(TAG, "request setPropFloat32 called "+ propFloat32); + if (m_propFloat32 != propFloat32) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropFloat32.getValue(); + Bundle data = new Bundle(); + + data.putFloat("propFloat32", propFloat32); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat32(float propFloat32) + { + Log.i(TAG, "value received from service for PropFloat32 "); + if (m_propFloat32 != propFloat32) + { + m_propFloat32 = propFloat32; + firePropFloat32Changed(propFloat32); + } + + } + + @Override + public float getPropFloat32() + { + Log.i(TAG, "request getPropFloat32 called, returning local"); + return m_propFloat32; + } + + + @Override + public void setPropFloat64(double propFloat64) + { + Log.i(TAG, "request setPropFloat64 called "+ propFloat64); + if (m_propFloat64 != propFloat64) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropFloat64.getValue(); + Bundle data = new Bundle(); + + data.putDouble("propFloat64", propFloat64); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat64(double propFloat64) + { + Log.i(TAG, "value received from service for PropFloat64 "); + if (m_propFloat64 != propFloat64) + { + m_propFloat64 = propFloat64; + firePropFloat64Changed(propFloat64); + } + + } + + @Override + public double getPropFloat64() + { + Log.i(TAG, "request getPropFloat64 called, returning local"); + return m_propFloat64; + } + + + @Override + public void setPropString(String propString) + { + Log.i(TAG, "request setPropString called "+ propString); + if (m_propString != propString) + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.PROP_PropString.getValue(); + Bundle data = new Bundle(); + + data.putString("propString", propString); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropString(String propString) + { + Log.i(TAG, "value received from service for PropString "); + if (m_propString != propString) + { + m_propString = propString; + firePropStringChanged(propString); + } + + } + + @Override + public String getPropString() + { + Log.i(TAG, "request getPropString called, returning local"); + return m_propString; + } + + + // methods + + + @Override + public void funcNoReturnValue(boolean paramBool) { + CompletableFuture resFuture = funcNoReturnValueAsync(paramBool); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { + + Log.i(TAG, "Call on service funcNoReturnValue "+ " " + paramBool); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncNoReturnValueReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve funcNoReturnValue"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public boolean funcBool(boolean paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + boolean result = bundle.getBoolean("result", false); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int funcInt(int paramInt) { + CompletableFuture resFuture = funcIntAsync(paramInt); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcIntAsync(int paramInt) { + + Log.i(TAG, "Call on service funcInt "+ " " + paramInt); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncIntReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("paramInt", paramInt); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve funcInt" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int funcInt32(int paramInt32) { + CompletableFuture resFuture = funcInt32Async(paramInt32); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcInt32Async(int paramInt32) { + + Log.i(TAG, "Call on service funcInt32 "+ " " + paramInt32); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncInt32Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("paramInt32", paramInt32); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve funcInt32" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public long funcInt64(long paramInt64) { + CompletableFuture resFuture = funcInt64Async(paramInt64); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcInt64Async(long paramInt64) { + + Log.i(TAG, "Call on service funcInt64 "+ " " + paramInt64); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncInt64Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putLong("paramInt64", paramInt64); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + long result = bundle.getLong("result", 0L); + Log.v(TAG, "resolve funcInt64" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public float funcFloat(float paramFloat) { + CompletableFuture resFuture = funcFloatAsync(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloatAsync(float paramFloat) { + + Log.i(TAG, "Call on service funcFloat "+ " " + paramFloat); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncFloatReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putFloat("paramFloat", paramFloat); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + float result = bundle.getFloat("result", 0.0f); + Log.v(TAG, "resolve funcFloat" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public float funcFloat32(float paramFloat32) { + CompletableFuture resFuture = funcFloat32Async(paramFloat32); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloat32Async(float paramFloat32) { + + Log.i(TAG, "Call on service funcFloat32 "+ " " + paramFloat32); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncFloat32Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putFloat("paramFloat32", paramFloat32); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + float result = bundle.getFloat("result", 0.0f); + Log.v(TAG, "resolve funcFloat32" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public double funcFloat64(double paramFloat) { + CompletableFuture resFuture = funcFloat64Async(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloat64Async(double paramFloat) { + + Log.i(TAG, "Call on service funcFloat64 "+ " " + paramFloat); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncFloat64Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putDouble("paramFloat", paramFloat); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + double result = bundle.getDouble("result", 0.0); + Log.v(TAG, "resolve funcFloat64" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public String funcString(String paramString) { + CompletableFuture resFuture = funcStringAsync(paramString); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcStringAsync(String paramString) { + + Log.i(TAG, "Call on service funcString "+ " " + paramString); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncStringReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putString("paramString", paramString); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + String result = bundle.getString("result", new String()); + Log.v(TAG, "resolve funcString" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } + public void onSigInt(int paramInt) + { + Log.i(TAG, "onSigInt received from service"); + fireSigInt(paramInt); + } + public void onSigInt32(int paramInt32) + { + Log.i(TAG, "onSigInt32 received from service"); + fireSigInt32(paramInt32); + } + public void onSigInt64(long paramInt64) + { + Log.i(TAG, "onSigInt64 received from service"); + fireSigInt64(paramInt64); + } + public void onSigFloat(float paramFloat) + { + Log.i(TAG, "onSigFloat received from service"); + fireSigFloat(paramFloat); + } + public void onSigFloat32(float paramFloat32) + { + Log.i(TAG, "onSigFloat32 received from service"); + fireSigFloat32(paramFloat32); + } + public void onSigFloat64(double paramFloat64) + { + Log.i(TAG, "onSigFloat64 received from service"); + fireSigFloat64(paramFloat64); + } + public void onSigString(String paramString) + { + Log.i(TAG, "onSigString received from service"); + fireSigString(paramString); + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java new file mode 100644 index 0000000..e7f3c15 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java @@ -0,0 +1,272 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class VoidInterfaceClient extends AbstractVoidInterface implements ServiceConnection +{ + private static final String TAG = "VoidInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + + + public VoidInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type VoidInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, VoidInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, VoidInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (VoidInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigVoid: { + + Bundle data = msg.getData(); + onSigVoid(); + break; + } + case RPC_FuncVoidResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received VoidInterfaceMessageType.RPC_FuncVoidResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + // methods + + + @Override + public void funcVoid() { + CompletableFuture resFuture = funcVoidAsync(); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcVoidAsync() { + + Log.i(TAG, "Call on service funcVoid "); + Message msg = new Message(); + msg.what = VoidInterfaceMessageType.RPC_FuncVoidReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve funcVoid"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigVoid() + { + Log.i(TAG, "onSigVoid received from service"); + fireSigVoid(); + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java new file mode 100644 index 0000000..427bac5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java @@ -0,0 +1,146 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.EmptyInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IEmptyInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class EmptyInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private EmptyInterfaceClient testedClient; + private IEmptyInterfaceEventListener listenerMock = mock(IEmptyInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IEmptyInterfaceClientMessageGetter serviceMessagesStorage = mock(IEmptyInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(EmptyInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IEmptyInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new EmptyInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(EmptyInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, EmptyInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java new file mode 100644 index 0000000..4c3bb06 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java @@ -0,0 +1,246 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.NoOperationsInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INoOperationsInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NoOperationsInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private NoOperationsInterfaceClient testedClient; + private INoOperationsInterfaceEventListener listenerMock = mock(INoOperationsInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INoOperationsInterfaceClientMessageGetter serviceMessagesStorage = mock(INoOperationsInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NoOperationsInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INoOperationsInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NoOperationsInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NoOperationsInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + } + + @Test + public void setPropertyRequestpropBool() + { + boolean testpropBool = true; + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedpropBool = data.getBoolean("propBool", false); + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + } + + @Test + public void setPropertyRequestpropInt() + { + int testpropInt = 1; + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropInt, testpropInt); + } + @Test + public void whenNotifiedsigVoid() throws RemoteException + { + + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.SIG_SigVoid.getValue()); + Bundle data = new Bundle(); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigVoid(); + +} + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool(testparamBool); + +} + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java new file mode 100644 index 0000000..8283a47 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java @@ -0,0 +1,257 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.NoPropertiesInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INoPropertiesInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NoPropertiesInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private NoPropertiesInterfaceClient testedClient; + private INoPropertiesInterfaceEventListener listenerMock = mock(INoPropertiesInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INoPropertiesInterfaceClientMessageGetter serviceMessagesStorage = mock(INoPropertiesInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NoPropertiesInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INoPropertiesInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NoPropertiesInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NoPropertiesInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + } + @Test + public void whenNotifiedsigVoid() throws RemoteException + { + + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.SIG_SigVoid.getValue()); + Bundle data = new Bundle(); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigVoid(); + +} + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool(testparamBool); + +} + + + public void onfuncVoidRequest() throws RemoteException { + + // Execute method + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcVoidAsync(); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NoPropertiesInterfaceMessageType.RPC_FuncVoidReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.RPC_FuncVoidResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + boolean testparamBool = true; + boolean expectedResult = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.booleanValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NoPropertiesInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putBoolean("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java new file mode 100644 index 0000000..2ddd408 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java @@ -0,0 +1,297 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.NoSignalsInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INoSignalsInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NoSignalsInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private NoSignalsInterfaceClient testedClient; + private INoSignalsInterfaceEventListener listenerMock = mock(INoSignalsInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INoSignalsInterfaceClientMessageGetter serviceMessagesStorage = mock(INoSignalsInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NoSignalsInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INoSignalsInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NoSignalsInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NoSignalsInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + } + + @Test + public void setPropertyRequestpropBool() + { + boolean testpropBool = true; + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedpropBool = data.getBoolean("propBool", false); + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + } + + @Test + public void setPropertyRequestpropInt() + { + int testpropInt = 1; + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropInt, testpropInt); + } + + + public void onfuncVoidRequest() throws RemoteException { + + // Execute method + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcVoidAsync(); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NoSignalsInterfaceMessageType.RPC_FuncVoidReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.RPC_FuncVoidResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + boolean testparamBool = true; + boolean expectedResult = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.booleanValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NoSignalsInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putBoolean("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java new file mode 100644 index 0000000..51ce211 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java @@ -0,0 +1,989 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.SimpleArrayInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISimpleArrayInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SimpleArrayInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SimpleArrayInterfaceClient testedClient; + private ISimpleArrayInterfaceEventListener listenerMock = mock(ISimpleArrayInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISimpleArrayInterfaceClientMessageGetter serviceMessagesStorage = mock(ISimpleArrayInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISimpleArrayInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SimpleArrayInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + boolean[] testpropBool = new boolean[1]; + testpropBool[0] = true; + data.putBooleanArray("propBool", testpropBool); + int[] testpropInt = new int[1]; + testpropInt[0] = 1; + data.putIntArray("propInt", testpropInt); + int[] testpropInt32 = new int[1]; + testpropInt32[0] = 1; + data.putIntArray("propInt32", testpropInt32); + long[] testpropInt64 = new long[1]; + testpropInt64[0] = 1L; + data.putLongArray("propInt64", testpropInt64); + float[] testpropFloat = new float[1]; + testpropFloat[0] = 1.0f; + data.putFloatArray("propFloat", testpropFloat); + float[] testpropFloat32 = new float[1]; + testpropFloat32[0] = 1.0f; + data.putFloatArray("propFloat32", testpropFloat32); + double[] testpropFloat64 = new double[1]; + testpropFloat64[0] = 1.0; + data.putDoubleArray("propFloat64", testpropFloat64); + String[] testpropString = new String[1]; + testpropString[0] = new String("xyz"); + data.putStringArray("propString", testpropString); + String testpropReadOnlyString = new String("xyz"); + data.putString("propReadOnlyString", testpropReadOnlyString); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt32Changed(testpropInt32); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt64Changed(testpropInt64); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(testpropFloat); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat32Changed(testpropFloat32); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); + inOrderEventListener.verify(listenerMock,times(1)).onPropReadOnlyStringChanged(testpropReadOnlyString); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + boolean[] testpropBool = new boolean[1]; + testpropBool[0] = true; + data.putBooleanArray("propBool", testpropBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + } + + @Test + public void setPropertyRequestpropBool() + { + boolean[] testpropBool = new boolean[1]; + testpropBool[0] = true; + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean[] receivedpropBool = data.getBooleanArray("propBool"); + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + int[] testpropInt = new int[1]; + testpropInt[0] = 1; + data.putIntArray("propInt", testpropInt); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + } + + @Test + public void setPropertyRequestpropInt() + { + int[] testpropInt = new int[1]; + testpropInt[0] = 1; + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + int[] receivedpropInt = data.getIntArray("propInt"); + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropInt32.getValue()); + Bundle data = new Bundle(); + int[] testpropInt32 = new int[1]; + testpropInt32[0] = 1; + data.putIntArray("propInt32", testpropInt32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt32Changed(testpropInt32); + } + + @Test + public void setPropertyRequestpropInt32() + { + int[] testpropInt32 = new int[1]; + testpropInt32[0] = 1; + + testedClient.setPropInt32(testpropInt32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropInt32.getValue(), response.what); + Bundle data = response.getData(); + + int[] receivedpropInt32 = data.getIntArray("propInt32"); + assertEquals(receivedpropInt32, testpropInt32); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropInt64.getValue()); + Bundle data = new Bundle(); + long[] testpropInt64 = new long[1]; + testpropInt64[0] = 1L; + data.putLongArray("propInt64", testpropInt64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt64Changed(testpropInt64); + } + + @Test + public void setPropertyRequestpropInt64() + { + long[] testpropInt64 = new long[1]; + testpropInt64[0] = 1L; + + testedClient.setPropInt64(testpropInt64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropInt64.getValue(), response.what); + Bundle data = response.getData(); + + long[] receivedpropInt64 = data.getLongArray("propInt64"); + assertEquals(receivedpropInt64, testpropInt64); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropFloat.getValue()); + Bundle data = new Bundle(); + float[] testpropFloat = new float[1]; + testpropFloat[0] = 1.0f; + data.putFloatArray("propFloat", testpropFloat); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(testpropFloat); + } + + @Test + public void setPropertyRequestpropFloat() + { + float[] testpropFloat = new float[1]; + testpropFloat[0] = 1.0f; + + testedClient.setPropFloat(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + float[] receivedpropFloat = data.getFloatArray("propFloat"); + assertEquals(receivedpropFloat, testpropFloat); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropFloat32.getValue()); + Bundle data = new Bundle(); + float[] testpropFloat32 = new float[1]; + testpropFloat32[0] = 1.0f; + data.putFloatArray("propFloat32", testpropFloat32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat32Changed(testpropFloat32); + } + + @Test + public void setPropertyRequestpropFloat32() + { + float[] testpropFloat32 = new float[1]; + testpropFloat32[0] = 1.0f; + + testedClient.setPropFloat32(testpropFloat32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropFloat32.getValue(), response.what); + Bundle data = response.getData(); + + float[] receivedpropFloat32 = data.getFloatArray("propFloat32"); + assertEquals(receivedpropFloat32, testpropFloat32); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropFloat64.getValue()); + Bundle data = new Bundle(); + double[] testpropFloat64 = new double[1]; + testpropFloat64[0] = 1.0; + data.putDoubleArray("propFloat64", testpropFloat64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); + } + + @Test + public void setPropertyRequestpropFloat64() + { + double[] testpropFloat64 = new double[1]; + testpropFloat64[0] = 1.0; + + testedClient.setPropFloat64(testpropFloat64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropFloat64.getValue(), response.what); + Bundle data = response.getData(); + + double[] receivedpropFloat64 = data.getDoubleArray("propFloat64"); + assertEquals(receivedpropFloat64, testpropFloat64); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropString.getValue()); + Bundle data = new Bundle(); + String[] testpropString = new String[1]; + testpropString[0] = new String("xyz"); + data.putStringArray("propString", testpropString); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); + } + + @Test + public void setPropertyRequestpropString() + { + String[] testpropString = new String[1]; + testpropString[0] = new String("xyz"); + + testedClient.setPropString(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropString.getValue(), response.what); + Bundle data = response.getData(); + + String[] receivedpropString = data.getStringArray("propString"); + assertEquals(receivedpropString, testpropString); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropReadOnlyStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SET_PropReadOnlyString.getValue()); + Bundle data = new Bundle(); + String testpropReadOnlyString = new String("xyz"); + data.putString("propReadOnlyString", testpropReadOnlyString); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropReadOnlyStringChanged(testpropReadOnlyString); + } + + @Test + public void setPropertyRequestpropReadOnlyString() + { + String testpropReadOnlyString = new String("xyz"); + + testedClient.setPropReadOnlyString(testpropReadOnlyString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.PROP_PropReadOnlyString.getValue(), response.what); + Bundle data = response.getData(); + + String receivedpropReadOnlyString = data.getString("propReadOnlyString", new String()); + assertEquals(receivedpropReadOnlyString, testpropReadOnlyString); + } + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + boolean[] testparamBool = new boolean[1]; + testparamBool[0] = true; + data.putBooleanArray("paramBool", testparamBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool(testparamBool); + +} + @Test + public void whenNotifiedsigInt() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigInt.getValue()); + Bundle data = new Bundle(); + int[] testparamInt = new int[1]; + testparamInt[0] = 1; + data.putIntArray("paramInt", testparamInt); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt(testparamInt); + +} + @Test + public void whenNotifiedsigInt32() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigInt32.getValue()); + Bundle data = new Bundle(); + int[] testparamInt32 = new int[1]; + testparamInt32[0] = 1; + data.putIntArray("paramInt32", testparamInt32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt32(testparamInt32); + +} + @Test + public void whenNotifiedsigInt64() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigInt64.getValue()); + Bundle data = new Bundle(); + long[] testparamInt64 = new long[1]; + testparamInt64[0] = 1L; + data.putLongArray("paramInt64", testparamInt64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt64(testparamInt64); + +} + @Test + public void whenNotifiedsigFloat() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigFloat.getValue()); + Bundle data = new Bundle(); + float[] testparamFloat = new float[1]; + testparamFloat[0] = 1.0f; + data.putFloatArray("paramFloat", testparamFloat); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat(testparamFloat); + +} + @Test + public void whenNotifiedsigFloat32() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigFloat32.getValue()); + Bundle data = new Bundle(); + float[] testparamFloa32 = new float[1]; + testparamFloa32[0] = 1.0f; + data.putFloatArray("paramFloa32", testparamFloa32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat32(testparamFloa32); + +} + @Test + public void whenNotifiedsigFloat64() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigFloat64.getValue()); + Bundle data = new Bundle(); + double[] testparamFloat64 = new double[1]; + testparamFloat64[0] = 1.0; + data.putDoubleArray("paramFloat64", testparamFloat64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat64(testparamFloat64); + +} + @Test + public void whenNotifiedsigString() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.SIG_SigString.getValue()); + Bundle data = new Bundle(); + String[] testparamString = new String[1]; + testparamString[0] = new String("xyz"); + data.putStringArray("paramString", testparamString); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigString(testparamString); + +} + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + boolean[] testparamBool = new boolean[1]; + testparamBool[0] = true; + boolean[] expectedResult = new boolean[1]; + expectedResult[0] = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean[] receivedparamBool = data.getBooleanArray("paramBool"); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putBooleanArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncIntRequest() throws RemoteException { + + // Execute method + int[] testparamInt = new int[1]; + testparamInt[0] = 1; + int[] expectedResult = new int[1]; + expectedResult[0] = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcIntAsync(testparamInt); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int[] receivedparamInt = data.getIntArray("paramInt"); + assertEquals(receivedparamInt, testparamInt); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncIntResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putIntArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncInt32Request() throws RemoteException { + + // Execute method + int[] testparamInt32 = new int[1]; + testparamInt32[0] = 1; + int[] expectedResult = new int[1]; + expectedResult[0] = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcInt32Async(testparamInt32); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncInt32Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int[] receivedparamInt32 = data.getIntArray("paramInt32"); + assertEquals(receivedparamInt32, testparamInt32); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncInt32Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putIntArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncInt64Request() throws RemoteException { + + // Execute method + long[] testparamInt64 = new long[1]; + testparamInt64[0] = 1L; + long[] expectedResult = new long[1]; + expectedResult[0] = 1L; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcInt64Async(testparamInt64); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncInt64Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + long[] receivedparamInt64 = data.getLongArray("paramInt64"); + assertEquals(receivedparamInt64, testparamInt64); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncInt64Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putLongArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloatRequest() throws RemoteException { + + // Execute method + float[] testparamFloat = new float[1]; + testparamFloat[0] = 1.0f; + float[] expectedResult = new float[1]; + expectedResult[0] = 1.0f; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloatAsync(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + float[] receivedparamFloat = data.getFloatArray("paramFloat"); + assertEquals(receivedparamFloat, testparamFloat); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncFloatResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putFloatArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloat32Request() throws RemoteException { + + // Execute method + float[] testparamFloat32 = new float[1]; + testparamFloat32[0] = 1.0f; + float[] expectedResult = new float[1]; + expectedResult[0] = 1.0f; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloat32Async(testparamFloat32); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloat32Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + float[] receivedparamFloat32 = data.getFloatArray("paramFloat32"); + assertEquals(receivedparamFloat32, testparamFloat32); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncFloat32Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putFloatArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloat64Request() throws RemoteException { + + // Execute method + double[] testparamFloat = new double[1]; + testparamFloat[0] = 1.0; + double[] expectedResult = new double[1]; + expectedResult[0] = 1.0; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloat64Async(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloat64Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + double[] receivedparamFloat = data.getDoubleArray("paramFloat"); + assertEquals(receivedparamFloat, testparamFloat); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncFloat64Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putDoubleArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncStringRequest() throws RemoteException { + + // Execute method + String[] testparamString = new String[1]; + testparamString[0] = new String("xyz"); + String[] expectedResult = new String[1]; + expectedResult[0] = new String("xyz"); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcStringAsync(testparamString); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + String[] receivedparamString = data.getStringArray("paramString"); + assertEquals(receivedparamString, testparamString); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncStringResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putStringArray("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java new file mode 100644 index 0000000..55438b0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java @@ -0,0 +1,947 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.SimpleInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISimpleInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SimpleInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private SimpleInterfaceClient testedClient; + private ISimpleInterfaceEventListener listenerMock = mock(ISimpleInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISimpleInterfaceClientMessageGetter serviceMessagesStorage = mock(ISimpleInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISimpleInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SimpleInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + int testpropInt32 = 1; + data.putInt("propInt32", testpropInt32); + long testpropInt64 = 1L; + data.putLong("propInt64", testpropInt64); + float testpropFloat = 1.0f; + data.putFloat("propFloat", testpropFloat); + float testpropFloat32 = 1.0f; + data.putFloat("propFloat32", testpropFloat32); + double testpropFloat64 = 1.0; + data.putDouble("propFloat64", testpropFloat64); + String testpropString = new String("xyz"); + data.putString("propString", testpropString); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt32Changed(testpropInt32); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt64Changed(testpropInt64); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(testpropFloat); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat32Changed(testpropFloat32); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); + } + + @Test + public void setPropertyRequestpropBool() + { + boolean testpropBool = true; + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedpropBool = data.getBoolean("propBool", false); + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); + } + + @Test + public void setPropertyRequestpropInt() + { + int testpropInt = 1; + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropInt32.getValue()); + Bundle data = new Bundle(); + int testpropInt32 = 1; + data.putInt("propInt32", testpropInt32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt32Changed(testpropInt32); + } + + @Test + public void setPropertyRequestpropInt32() + { + int testpropInt32 = 1; + + testedClient.setPropInt32(testpropInt32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropInt32.getValue(), response.what); + Bundle data = response.getData(); + + int receivedpropInt32 = data.getInt("propInt32", 0); + assertEquals(receivedpropInt32, testpropInt32); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropInt64.getValue()); + Bundle data = new Bundle(); + long testpropInt64 = 1L; + data.putLong("propInt64", testpropInt64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropInt64Changed(testpropInt64); + } + + @Test + public void setPropertyRequestpropInt64() + { + long testpropInt64 = 1L; + + testedClient.setPropInt64(testpropInt64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropInt64.getValue(), response.what); + Bundle data = response.getData(); + + long receivedpropInt64 = data.getLong("propInt64", 0L); + assertEquals(receivedpropInt64, testpropInt64); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropFloat.getValue()); + Bundle data = new Bundle(); + float testpropFloat = 1.0f; + data.putFloat("propFloat", testpropFloat); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(testpropFloat); + } + + @Test + public void setPropertyRequestpropFloat() + { + float testpropFloat = 1.0f; + + testedClient.setPropFloat(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + float receivedpropFloat = data.getFloat("propFloat", 0.0f); + assertEquals(receivedpropFloat, testpropFloat, 1e-6f); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropFloat32.getValue()); + Bundle data = new Bundle(); + float testpropFloat32 = 1.0f; + data.putFloat("propFloat32", testpropFloat32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat32Changed(testpropFloat32); + } + + @Test + public void setPropertyRequestpropFloat32() + { + float testpropFloat32 = 1.0f; + + testedClient.setPropFloat32(testpropFloat32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropFloat32.getValue(), response.what); + Bundle data = response.getData(); + + float receivedpropFloat32 = data.getFloat("propFloat32", 0.0f); + assertEquals(receivedpropFloat32, testpropFloat32, 1e-6f); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropFloat64.getValue()); + Bundle data = new Bundle(); + double testpropFloat64 = 1.0; + data.putDouble("propFloat64", testpropFloat64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); + } + + @Test + public void setPropertyRequestpropFloat64() + { + double testpropFloat64 = 1.0; + + testedClient.setPropFloat64(testpropFloat64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropFloat64.getValue(), response.what); + Bundle data = response.getData(); + + double receivedpropFloat64 = data.getDouble("propFloat64", 0.0); + assertEquals(receivedpropFloat64, testpropFloat64, 1e-6f); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SET_PropString.getValue()); + Bundle data = new Bundle(); + String testpropString = new String("xyz"); + data.putString("propString", testpropString); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); + } + + @Test + public void setPropertyRequestpropString() + { + String testpropString = new String("xyz"); + + testedClient.setPropString(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.PROP_PropString.getValue(), response.what); + Bundle data = response.getData(); + + String receivedpropString = data.getString("propString", new String()); + assertEquals(receivedpropString, testpropString); + } + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool(testparamBool); + +} + @Test + public void whenNotifiedsigInt() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigInt.getValue()); + Bundle data = new Bundle(); + int testparamInt = 1; + data.putInt("paramInt", testparamInt); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt(testparamInt); + +} + @Test + public void whenNotifiedsigInt32() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigInt32.getValue()); + Bundle data = new Bundle(); + int testparamInt32 = 1; + data.putInt("paramInt32", testparamInt32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt32(testparamInt32); + +} + @Test + public void whenNotifiedsigInt64() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigInt64.getValue()); + Bundle data = new Bundle(); + long testparamInt64 = 1L; + data.putLong("paramInt64", testparamInt64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt64(testparamInt64); + +} + @Test + public void whenNotifiedsigFloat() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigFloat.getValue()); + Bundle data = new Bundle(); + float testparamFloat = 1.0f; + data.putFloat("paramFloat", testparamFloat); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat(testparamFloat); + +} + @Test + public void whenNotifiedsigFloat32() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigFloat32.getValue()); + Bundle data = new Bundle(); + float testparamFloat32 = 1.0f; + data.putFloat("paramFloat32", testparamFloat32); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat32(testparamFloat32); + +} + @Test + public void whenNotifiedsigFloat64() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigFloat64.getValue()); + Bundle data = new Bundle(); + double testparamFloat64 = 1.0; + data.putDouble("paramFloat64", testparamFloat64); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat64(testparamFloat64); + +} + @Test + public void whenNotifiedsigString() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.SIG_SigString.getValue()); + Bundle data = new Bundle(); + String testparamString = new String("xyz"); + data.putString("paramString", testparamString); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigString(testparamString); + +} + + + public void onfuncNoReturnValueRequest() throws RemoteException { + + // Execute method + boolean testparamBool = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcNoReturnValueAsync(testparamBool); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncNoReturnValueReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncNoReturnValueResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + boolean testparamBool = true; + boolean expectedResult = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.booleanValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putBoolean("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncIntRequest() throws RemoteException { + + // Execute method + int testparamInt = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcIntAsync(testparamInt); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int receivedparamInt = data.getInt("paramInt", 0); + assertEquals(receivedparamInt, testparamInt); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncIntResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncInt32Request() throws RemoteException { + + // Execute method + int testparamInt32 = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcInt32Async(testparamInt32); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncInt32Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int receivedparamInt32 = data.getInt("paramInt32", 0); + assertEquals(receivedparamInt32, testparamInt32); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncInt32Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncInt64Request() throws RemoteException { + + // Execute method + long testparamInt64 = 1L; + long expectedResult = 1L; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcInt64Async(testparamInt64); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.longValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncInt64Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + long receivedparamInt64 = data.getLong("paramInt64", 0L); + assertEquals(receivedparamInt64, testparamInt64); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncInt64Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putLong("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloatRequest() throws RemoteException { + + // Execute method + float testparamFloat = 1.0f; + float expectedResult = 1.0f; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloatAsync(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.floatValue(), 1e-6f); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + float receivedparamFloat = data.getFloat("paramFloat", 0.0f); + assertEquals(receivedparamFloat, testparamFloat, 1e-6f); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncFloatResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putFloat("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloat32Request() throws RemoteException { + + // Execute method + float testparamFloat32 = 1.0f; + float expectedResult = 1.0f; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloat32Async(testparamFloat32); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.floatValue(), 1e-6f); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncFloat32Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + float receivedparamFloat32 = data.getFloat("paramFloat32", 0.0f); + assertEquals(receivedparamFloat32, testparamFloat32, 1e-6f); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncFloat32Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putFloat("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloat64Request() throws RemoteException { + + // Execute method + double testparamFloat = 1.0; + double expectedResult = 1.0; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloat64Async(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.doubleValue(), 1e-6f); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncFloat64Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + double receivedparamFloat = data.getDouble("paramFloat", 0.0); + assertEquals(receivedparamFloat, testparamFloat, 1e-6f); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncFloat64Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putDouble("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncStringRequest() throws RemoteException { + + // Execute method + String testparamString = new String("xyz"); + String expectedResult = new String("xyz"); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcStringAsync(testparamString); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + String receivedparamString = data.getString("paramString", new String()); + assertEquals(receivedparamString, testparamString); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncStringResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putString("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java new file mode 100644 index 0000000..880fb84 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java @@ -0,0 +1,197 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbSimple.tbSimple_android_client; + +import tbSimple.tbSimple_android_client.VoidInterfaceClient; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IVoidInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class VoidInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private VoidInterfaceClient testedClient; + private IVoidInterfaceEventListener listenerMock = mock(IVoidInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IVoidInterfaceClientMessageGetter serviceMessagesStorage = mock(IVoidInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(VoidInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IVoidInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new VoidInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbSimple.tbSimple_android_service", "tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(VoidInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, VoidInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + } + @Test + public void whenNotifiedsigVoid() throws RemoteException + { + + Message msg = Message.obtain(null, VoidInterfaceMessageType.SIG_SigVoid.getValue()); + Bundle data = new Bundle(); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigVoid(); + +} + + + public void onfuncVoidRequest() throws RemoteException { + + // Execute method + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcVoidAsync(); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(VoidInterfaceMessageType.RPC_FuncVoidReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, VoidInterfaceMessageType.RPC_FuncVoidResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/additions.gradle b/goldenmaster/tbSimple/tbSimple_android_messenger/additions.gradle new file mode 100644 index 0000000..d18c14a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbSimple.tbSimple_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/build.gradle b/goldenmaster/tbSimple/tbSimple_android_messenger/build.gradle new file mode 100644 index 0000000..a15200d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSimple" +version = "1.0.0" + +android { + namespace 'tbSimple.tbSimple_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceMessageType.java new file mode 100644 index 0000000..d376f8c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceMessageType.java @@ -0,0 +1,30 @@ +package tbSimple.tbSimple_android_messenger; + +public enum EmptyInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + EmptyInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + EmptyInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static EmptyInterfaceMessageType fromInteger(int value) + { + for (EmptyInterfaceMessageType event : EmptyInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return EmptyInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceMessageType.java new file mode 100644 index 0000000..066cabe --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceMessageType.java @@ -0,0 +1,36 @@ +package tbSimple.tbSimple_android_messenger; + +public enum NoOperationsInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + SIG_SigVoid(7), + SIG_SigBool(8), + NoOperationsInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NoOperationsInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NoOperationsInterfaceMessageType fromInteger(int value) + { + for (NoOperationsInterfaceMessageType event : NoOperationsInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NoOperationsInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceMessageType.java new file mode 100644 index 0000000..a0a20d3 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceMessageType.java @@ -0,0 +1,36 @@ +package tbSimple.tbSimple_android_messenger; + +public enum NoPropertiesInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + SIG_SigVoid(3), + SIG_SigBool(4), + RPC_FuncVoidReq(5), + RPC_FuncVoidResp(6), + RPC_FuncBoolReq(7), + RPC_FuncBoolResp(8), + NoPropertiesInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NoPropertiesInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NoPropertiesInterfaceMessageType fromInteger(int value) + { + for (NoPropertiesInterfaceMessageType event : NoPropertiesInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NoPropertiesInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceMessageType.java new file mode 100644 index 0000000..ceb7cf3 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceMessageType.java @@ -0,0 +1,38 @@ +package tbSimple.tbSimple_android_messenger; + +public enum NoSignalsInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + RPC_FuncVoidReq(7), + RPC_FuncVoidResp(8), + RPC_FuncBoolReq(9), + RPC_FuncBoolResp(10), + NoSignalsInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NoSignalsInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NoSignalsInterfaceMessageType fromInteger(int value) + { + for (NoSignalsInterfaceMessageType event : NoSignalsInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NoSignalsInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceMessageType.java new file mode 100644 index 0000000..9a1988b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceMessageType.java @@ -0,0 +1,72 @@ +package tbSimple.tbSimple_android_messenger; + +public enum SimpleArrayInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + PROP_PropInt32(7), + SET_PropInt32(8), + PROP_PropInt64(9), + SET_PropInt64(10), + PROP_PropFloat(11), + SET_PropFloat(12), + PROP_PropFloat32(13), + SET_PropFloat32(14), + PROP_PropFloat64(15), + SET_PropFloat64(16), + PROP_PropString(17), + SET_PropString(18), + PROP_PropReadOnlyString(19), + SET_PropReadOnlyString(20), + SIG_SigBool(21), + SIG_SigInt(22), + SIG_SigInt32(23), + SIG_SigInt64(24), + SIG_SigFloat(25), + SIG_SigFloat32(26), + SIG_SigFloat64(27), + SIG_SigString(28), + RPC_FuncBoolReq(29), + RPC_FuncBoolResp(30), + RPC_FuncIntReq(31), + RPC_FuncIntResp(32), + RPC_FuncInt32Req(33), + RPC_FuncInt32Resp(34), + RPC_FuncInt64Req(35), + RPC_FuncInt64Resp(36), + RPC_FuncFloatReq(37), + RPC_FuncFloatResp(38), + RPC_FuncFloat32Req(39), + RPC_FuncFloat32Resp(40), + RPC_FuncFloat64Req(41), + RPC_FuncFloat64Resp(42), + RPC_FuncStringReq(43), + RPC_FuncStringResp(44), + SimpleArrayInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SimpleArrayInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SimpleArrayInterfaceMessageType fromInteger(int value) + { + for (SimpleArrayInterfaceMessageType event : SimpleArrayInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SimpleArrayInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java new file mode 100644 index 0000000..917dcc3 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java @@ -0,0 +1,72 @@ +package tbSimple.tbSimple_android_messenger; + +public enum SimpleInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + PROP_PropInt32(7), + SET_PropInt32(8), + PROP_PropInt64(9), + SET_PropInt64(10), + PROP_PropFloat(11), + SET_PropFloat(12), + PROP_PropFloat32(13), + SET_PropFloat32(14), + PROP_PropFloat64(15), + SET_PropFloat64(16), + PROP_PropString(17), + SET_PropString(18), + SIG_SigBool(19), + SIG_SigInt(20), + SIG_SigInt32(21), + SIG_SigInt64(22), + SIG_SigFloat(23), + SIG_SigFloat32(24), + SIG_SigFloat64(25), + SIG_SigString(26), + RPC_FuncNoReturnValueReq(27), + RPC_FuncNoReturnValueResp(28), + RPC_FuncBoolReq(29), + RPC_FuncBoolResp(30), + RPC_FuncIntReq(31), + RPC_FuncIntResp(32), + RPC_FuncInt32Req(33), + RPC_FuncInt32Resp(34), + RPC_FuncInt64Req(35), + RPC_FuncInt64Resp(36), + RPC_FuncFloatReq(37), + RPC_FuncFloatResp(38), + RPC_FuncFloat32Req(39), + RPC_FuncFloat32Resp(40), + RPC_FuncFloat64Req(41), + RPC_FuncFloat64Resp(42), + RPC_FuncStringReq(43), + RPC_FuncStringResp(44), + SimpleInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SimpleInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SimpleInterfaceMessageType fromInteger(int value) + { + for (SimpleInterfaceMessageType event : SimpleInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SimpleInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceMessageType.java new file mode 100644 index 0000000..3bcb042 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceMessageType.java @@ -0,0 +1,33 @@ +package tbSimple.tbSimple_android_messenger; + +public enum VoidInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + SIG_SigVoid(3), + RPC_FuncVoidReq(4), + RPC_FuncVoidResp(5), + VoidInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + VoidInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static VoidInterfaceMessageType fromInteger(int value) + { + for (VoidInterfaceMessageType event : VoidInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return VoidInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle b/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle new file mode 100644 index 0000000..64f796a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbSimple.tbSimple_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + implementation project(':tbSimple_impl') + implementation project(':tbSimple_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/build.gradle b/goldenmaster/tbSimple/tbSimple_android_service/build.gradle new file mode 100644 index 0000000..7a9715a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSimple" +version = "1.0.0" + + +android { + namespace 'tbSimple.tbSimple_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + implementation project(':tbSimple_impl') + implementation project(':tbSimple_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbSimple/tbSimple_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a97d070 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java new file mode 100644 index 0000000..523669a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java @@ -0,0 +1,228 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_android_service.IEmptyInterfaceServiceFactory; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class EmptyInterfaceServiceAdapter extends Service +{ + private static final String TAG = "EmptyInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IEmptyInterface mBackendService; + private static IEmptyInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public EmptyInterfaceServiceAdapter() + { + } + + public static IEmptyInterface setService(IEmptyInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(EmptyInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: EmptyInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(EmptyInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(EmptyInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IEmptyInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (EmptyInterfaceMessageType.fromInteger(msg.what) != EmptyInterfaceMessageType.REGISTER_CLIENT + && EmptyInterfaceMessageType.fromInteger(msg.what) != EmptyInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: EmptyInterfaceMessageType" + EmptyInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (EmptyInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = EmptyInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java new file mode 100644 index 0000000..5ea87a3 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.IEmptyInterfaceServiceFactory; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_impl.EmptyInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton EmptyInterfaceServiceFactory thread for the system. This is a thread for + * EmptyInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class EmptyInterfaceServiceFactory extends HandlerThread implements IEmptyInterfaceServiceFactory +{ + private EmptyInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static EmptyInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: EmptyInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractEmptyInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new EmptyInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new EmptyInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final EmptyInterfaceServiceFactory INSTANCE = createInstance(); + } + + private EmptyInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static EmptyInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + EmptyInterfaceServiceFactory t = new EmptyInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java new file mode 100644 index 0000000..0b32d98 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.EmptyInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class EmptyInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "EmptyInterfaceStarter"; + + + + public static IEmptyInterface start(Context context) { + stop(context); + androidService = new Intent(context, EmptyInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + EmptyInterfaceServiceFactory factory = EmptyInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for EmptyInterfaceServiceFactory"); + return EmptyInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IEmptyInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IEmptyInterfaceServiceFactory.java new file mode 100644 index 0000000..031a7b7 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IEmptyInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.IEmptyInterface; + + +public interface IEmptyInterfaceServiceFactory { + public IEmptyInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoOperationsInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoOperationsInterfaceServiceFactory.java new file mode 100644 index 0000000..c1785bd --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoOperationsInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.INoOperationsInterface; + + +public interface INoOperationsInterfaceServiceFactory { + public INoOperationsInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoPropertiesInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoPropertiesInterfaceServiceFactory.java new file mode 100644 index 0000000..e02550c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoPropertiesInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.INoPropertiesInterface; + + +public interface INoPropertiesInterfaceServiceFactory { + public INoPropertiesInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoSignalsInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoSignalsInterfaceServiceFactory.java new file mode 100644 index 0000000..54a897a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/INoSignalsInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.INoSignalsInterface; + + +public interface INoSignalsInterfaceServiceFactory { + public INoSignalsInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleArrayInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleArrayInterfaceServiceFactory.java new file mode 100644 index 0000000..506949b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleArrayInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.ISimpleArrayInterface; + + +public interface ISimpleArrayInterfaceServiceFactory { + public ISimpleArrayInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleInterfaceServiceFactory.java new file mode 100644 index 0000000..b5dd39b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/ISimpleInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.ISimpleInterface; + + +public interface ISimpleInterfaceServiceFactory { + public ISimpleInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IVoidInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IVoidInterfaceServiceFactory.java new file mode 100644 index 0000000..c19d466 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/IVoidInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_android_service; +import tbSimple.tbSimple_api.IVoidInterface; + + +public interface IVoidInterfaceServiceFactory { + public IVoidInterface getServiceInstance(); +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java new file mode 100644 index 0000000..11afffe --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java @@ -0,0 +1,294 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_android_service.INoOperationsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NoOperationsInterfaceServiceAdapter extends Service +{ + private static final String TAG = "NoOperationsInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INoOperationsInterface mBackendService; + private static INoOperationsInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NoOperationsInterfaceServiceAdapter() + { + } + + public static INoOperationsInterface setService(INoOperationsInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NoOperationsInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NoOperationsInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NoOperationsInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NoOperationsInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INoOperationsInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NoOperationsInterfaceMessageType.fromInteger(msg.what) != NoOperationsInterfaceMessageType.REGISTER_CLIENT + && NoOperationsInterfaceMessageType.fromInteger(msg.what) != NoOperationsInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NoOperationsInterfaceMessageType" + NoOperationsInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NoOperationsInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + + boolean propBool = data.getBoolean("propBool", false); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + + int propInt = data.getInt("propInt", 0); + mBackendService.setPropInt(propInt); + break; + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + boolean propBool = mBackendService.getPropBool(); + + data.putBoolean("propBool", propBool); + int propInt = mBackendService.getPropInt(); + + data.putInt("propInt", propInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(boolean propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("propBool", propBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(int propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt", propInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigVoid(){ + Log.i(TAG, "New singal for SigVoid = "); + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.SIG_SigVoid.getValue(); + Bundle data = new Bundle(); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(boolean paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = NoOperationsInterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java new file mode 100644 index 0000000..b76a0dc --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.INoOperationsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NoOperationsInterfaceServiceFactory thread for the system. This is a thread for + * NoOperationsInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NoOperationsInterfaceServiceFactory extends HandlerThread implements INoOperationsInterfaceServiceFactory +{ + private NoOperationsInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NoOperationsInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NoOperationsInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNoOperationsInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NoOperationsInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NoOperationsInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NoOperationsInterfaceServiceFactory INSTANCE = createInstance(); + } + + private NoOperationsInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NoOperationsInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NoOperationsInterfaceServiceFactory t = new NoOperationsInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java new file mode 100644 index 0000000..e95b214 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class NoOperationsInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NoOperationsInterfaceStarter"; + + + + public static INoOperationsInterface start(Context context) { + stop(context); + androidService = new Intent(context, NoOperationsInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NoOperationsInterfaceServiceFactory factory = NoOperationsInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for NoOperationsInterfaceServiceFactory"); + return NoOperationsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java new file mode 100644 index 0000000..7698db8 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java @@ -0,0 +1,300 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_android_service.INoPropertiesInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NoPropertiesInterfaceServiceAdapter extends Service +{ + private static final String TAG = "NoPropertiesInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INoPropertiesInterface mBackendService; + private static INoPropertiesInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NoPropertiesInterfaceServiceAdapter() + { + } + + public static INoPropertiesInterface setService(INoPropertiesInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NoPropertiesInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NoPropertiesInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NoPropertiesInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NoPropertiesInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INoPropertiesInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NoPropertiesInterfaceMessageType.fromInteger(msg.what) != NoPropertiesInterfaceMessageType.REGISTER_CLIENT + && NoPropertiesInterfaceMessageType.fromInteger(msg.what) != NoPropertiesInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NoPropertiesInterfaceMessageType" + NoPropertiesInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NoPropertiesInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncVoidReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + mBackendService.funcVoid(); + + Message respMsg = new Message(); + respMsg.what = NoPropertiesInterfaceMessageType.RPC_FuncVoidResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean paramBool = data.getBoolean("paramBool", false); + + boolean result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = NoPropertiesInterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putBoolean("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NoPropertiesInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigVoid(){ + Log.i(TAG, "New singal for SigVoid = "); + Message msg = new Message(); + msg.what = NoPropertiesInterfaceMessageType.SIG_SigVoid.getValue(); + Bundle data = new Bundle(); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(boolean paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = NoPropertiesInterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java new file mode 100644 index 0000000..39468ba --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.INoPropertiesInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NoPropertiesInterfaceServiceFactory thread for the system. This is a thread for + * NoPropertiesInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NoPropertiesInterfaceServiceFactory extends HandlerThread implements INoPropertiesInterfaceServiceFactory +{ + private NoPropertiesInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NoPropertiesInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NoPropertiesInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNoPropertiesInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NoPropertiesInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NoPropertiesInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NoPropertiesInterfaceServiceFactory INSTANCE = createInstance(); + } + + private NoPropertiesInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NoPropertiesInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NoPropertiesInterfaceServiceFactory t = new NoPropertiesInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java new file mode 100644 index 0000000..3b33dd1 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class NoPropertiesInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NoPropertiesInterfaceStarter"; + + + + public static INoPropertiesInterface start(Context context) { + stop(context); + androidService = new Intent(context, NoPropertiesInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NoPropertiesInterfaceServiceFactory factory = NoPropertiesInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for NoPropertiesInterfaceServiceFactory"); + return NoPropertiesInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java new file mode 100644 index 0000000..8254b3d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java @@ -0,0 +1,326 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_android_service.INoSignalsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NoSignalsInterfaceServiceAdapter extends Service +{ + private static final String TAG = "NoSignalsInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INoSignalsInterface mBackendService; + private static INoSignalsInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NoSignalsInterfaceServiceAdapter() + { + } + + public static INoSignalsInterface setService(INoSignalsInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NoSignalsInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NoSignalsInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NoSignalsInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NoSignalsInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INoSignalsInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NoSignalsInterfaceMessageType.fromInteger(msg.what) != NoSignalsInterfaceMessageType.REGISTER_CLIENT + && NoSignalsInterfaceMessageType.fromInteger(msg.what) != NoSignalsInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NoSignalsInterfaceMessageType" + NoSignalsInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NoSignalsInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + + boolean propBool = data.getBoolean("propBool", false); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + + int propInt = data.getInt("propInt", 0); + mBackendService.setPropInt(propInt); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncVoidReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + mBackendService.funcVoid(); + + Message respMsg = new Message(); + respMsg.what = NoSignalsInterfaceMessageType.RPC_FuncVoidResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean paramBool = data.getBoolean("paramBool", false); + + boolean result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = NoSignalsInterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putBoolean("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + boolean propBool = mBackendService.getPropBool(); + + data.putBoolean("propBool", propBool); + int propInt = mBackendService.getPropInt(); + + data.putInt("propInt", propInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(boolean propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("propBool", propBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(int propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = NoSignalsInterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt", propInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java new file mode 100644 index 0000000..7c78ec6 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.INoSignalsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NoSignalsInterfaceServiceFactory thread for the system. This is a thread for + * NoSignalsInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NoSignalsInterfaceServiceFactory extends HandlerThread implements INoSignalsInterfaceServiceFactory +{ + private NoSignalsInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NoSignalsInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NoSignalsInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNoSignalsInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NoSignalsInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NoSignalsInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NoSignalsInterfaceServiceFactory INSTANCE = createInstance(); + } + + private NoSignalsInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NoSignalsInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NoSignalsInterfaceServiceFactory t = new NoSignalsInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java new file mode 100644 index 0000000..7f732d8 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class NoSignalsInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NoSignalsInterfaceStarter"; + + + + public static INoSignalsInterface start(Context context) { + stop(context); + androidService = new Intent(context, NoSignalsInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NoSignalsInterfaceServiceFactory factory = NoSignalsInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for NoSignalsInterfaceServiceFactory"); + return NoSignalsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java new file mode 100644 index 0000000..075ae9a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java @@ -0,0 +1,747 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_android_service.ISimpleArrayInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SimpleArrayInterfaceServiceAdapter extends Service +{ + private static final String TAG = "SimpleArrayInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISimpleArrayInterface mBackendService; + private static ISimpleArrayInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SimpleArrayInterfaceServiceAdapter() + { + } + + public static ISimpleArrayInterface setService(ISimpleArrayInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SimpleArrayInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SimpleArrayInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleArrayInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleArrayInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISimpleArrayInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SimpleArrayInterfaceMessageType.fromInteger(msg.what) != SimpleArrayInterfaceMessageType.REGISTER_CLIENT + && SimpleArrayInterfaceMessageType.fromInteger(msg.what) != SimpleArrayInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SimpleArrayInterfaceMessageType" + SimpleArrayInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SimpleArrayInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + + boolean[] propBool = data.getBooleanArray("propBool"); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + + int[] propInt = data.getIntArray("propInt"); + mBackendService.setPropInt(propInt); + break; + } + case PROP_PropInt32: + { + Bundle data = msg.getData(); + + int[] propInt32 = data.getIntArray("propInt32"); + mBackendService.setPropInt32(propInt32); + break; + } + case PROP_PropInt64: + { + Bundle data = msg.getData(); + + long[] propInt64 = data.getLongArray("propInt64"); + mBackendService.setPropInt64(propInt64); + break; + } + case PROP_PropFloat: + { + Bundle data = msg.getData(); + + float[] propFloat = data.getFloatArray("propFloat"); + mBackendService.setPropFloat(propFloat); + break; + } + case PROP_PropFloat32: + { + Bundle data = msg.getData(); + + float[] propFloat32 = data.getFloatArray("propFloat32"); + mBackendService.setPropFloat32(propFloat32); + break; + } + case PROP_PropFloat64: + { + Bundle data = msg.getData(); + + double[] propFloat64 = data.getDoubleArray("propFloat64"); + mBackendService.setPropFloat64(propFloat64); + break; + } + case PROP_PropString: + { + Bundle data = msg.getData(); + + String[] propString = data.getStringArray("propString"); + mBackendService.setPropString(propString); + break; + } + case PROP_PropReadOnlyString: + { + Bundle data = msg.getData(); + + String propReadOnlyString = data.getString("propReadOnlyString", new String()); + mBackendService.setPropReadOnlyString(propReadOnlyString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean[] paramBool = data.getBooleanArray("paramBool"); + + boolean[] result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putBooleanArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncIntReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int[] paramInt = data.getIntArray("paramInt"); + + int[] result = mBackendService.funcInt(paramInt); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncIntResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putIntArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncInt32Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int[] paramInt32 = data.getIntArray("paramInt32"); + + int[] result = mBackendService.funcInt32(paramInt32); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncInt32Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putIntArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncInt64Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + long[] paramInt64 = data.getLongArray("paramInt64"); + + long[] result = mBackendService.funcInt64(paramInt64); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncInt64Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putLongArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloatReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + float[] paramFloat = data.getFloatArray("paramFloat"); + + float[] result = mBackendService.funcFloat(paramFloat); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncFloatResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putFloatArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloat32Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + float[] paramFloat32 = data.getFloatArray("paramFloat32"); + + float[] result = mBackendService.funcFloat32(paramFloat32); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncFloat32Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putFloatArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloat64Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + double[] paramFloat = data.getDoubleArray("paramFloat"); + + double[] result = mBackendService.funcFloat64(paramFloat); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncFloat64Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putDoubleArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncStringReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + String[] paramString = data.getStringArray("paramString"); + + String[] result = mBackendService.funcString(paramString); + + Message respMsg = new Message(); + respMsg.what = SimpleArrayInterfaceMessageType.RPC_FuncStringResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putStringArray("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + boolean[] propBool = mBackendService.getPropBool(); + + data.putBooleanArray("propBool", propBool); + int[] propInt = mBackendService.getPropInt(); + + data.putIntArray("propInt", propInt); + int[] propInt32 = mBackendService.getPropInt32(); + + data.putIntArray("propInt32", propInt32); + long[] propInt64 = mBackendService.getPropInt64(); + + data.putLongArray("propInt64", propInt64); + float[] propFloat = mBackendService.getPropFloat(); + + data.putFloatArray("propFloat", propFloat); + float[] propFloat32 = mBackendService.getPropFloat32(); + + data.putFloatArray("propFloat32", propFloat32); + double[] propFloat64 = mBackendService.getPropFloat64(); + + data.putDoubleArray("propFloat64", propFloat64); + String[] propString = mBackendService.getPropString(); + + data.putStringArray("propString", propString); + String propReadOnlyString = mBackendService.getPropReadOnlyString(); + + data.putString("propReadOnlyString", propReadOnlyString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(boolean[] propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBooleanArray("propBool", propBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(int[] propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putIntArray("propInt", propInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropInt32Changed(int[] propInt32){ + Log.i(TAG, "New value for PropInt32 from backend" + propInt32); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropInt32.getValue(); + Bundle data = new Bundle(); + + data.putIntArray("propInt32", propInt32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropInt64Changed(long[] propInt64){ + Log.i(TAG, "New value for PropInt64 from backend" + propInt64); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropInt64.getValue(); + Bundle data = new Bundle(); + + data.putLongArray("propInt64", propInt64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloatChanged(float[] propFloat){ + Log.i(TAG, "New value for PropFloat from backend" + propFloat); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putFloatArray("propFloat", propFloat); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloat32Changed(float[] propFloat32){ + Log.i(TAG, "New value for PropFloat32 from backend" + propFloat32); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropFloat32.getValue(); + Bundle data = new Bundle(); + + data.putFloatArray("propFloat32", propFloat32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloat64Changed(double[] propFloat64){ + Log.i(TAG, "New value for PropFloat64 from backend" + propFloat64); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropFloat64.getValue(); + Bundle data = new Bundle(); + + data.putDoubleArray("propFloat64", propFloat64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropStringChanged(String[] propString){ + Log.i(TAG, "New value for PropString from backend" + propString); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropString.getValue(); + Bundle data = new Bundle(); + + data.putStringArray("propString", propString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropReadOnlyStringChanged(String propReadOnlyString){ + Log.i(TAG, "New value for PropReadOnlyString from backend" + propReadOnlyString); + + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SET_PropReadOnlyString.getValue(); + Bundle data = new Bundle(); + + data.putString("propReadOnlyString", propReadOnlyString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(boolean[] paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putBooleanArray("paramBool", paramBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt(int[] paramInt){ + Log.i(TAG, "New singal for SigInt = "+ " " + paramInt); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigInt.getValue(); + Bundle data = new Bundle(); + + data.putIntArray("paramInt", paramInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt32(int[] paramInt32){ + Log.i(TAG, "New singal for SigInt32 = "+ " " + paramInt32); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigInt32.getValue(); + Bundle data = new Bundle(); + + data.putIntArray("paramInt32", paramInt32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt64(long[] paramInt64){ + Log.i(TAG, "New singal for SigInt64 = "+ " " + paramInt64); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigInt64.getValue(); + Bundle data = new Bundle(); + + data.putLongArray("paramInt64", paramInt64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat(float[] paramFloat){ + Log.i(TAG, "New singal for SigFloat = "+ " " + paramFloat); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigFloat.getValue(); + Bundle data = new Bundle(); + + data.putFloatArray("paramFloat", paramFloat); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat32(float[] paramFloa32){ + Log.i(TAG, "New singal for SigFloat32 = "+ " " + paramFloa32); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigFloat32.getValue(); + Bundle data = new Bundle(); + + data.putFloatArray("paramFloa32", paramFloa32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat64(double[] paramFloat64){ + Log.i(TAG, "New singal for SigFloat64 = "+ " " + paramFloat64); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigFloat64.getValue(); + Bundle data = new Bundle(); + + data.putDoubleArray("paramFloat64", paramFloat64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigString(String[] paramString){ + Log.i(TAG, "New singal for SigString = "+ " " + paramString); + Message msg = new Message(); + msg.what = SimpleArrayInterfaceMessageType.SIG_SigString.getValue(); + Bundle data = new Bundle(); + + data.putStringArray("paramString", paramString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java new file mode 100644 index 0000000..7c7db5e --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.ISimpleArrayInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SimpleArrayInterfaceServiceFactory thread for the system. This is a thread for + * SimpleArrayInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SimpleArrayInterfaceServiceFactory extends HandlerThread implements ISimpleArrayInterfaceServiceFactory +{ + private SimpleArrayInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SimpleArrayInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SimpleArrayInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSimpleArrayInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SimpleArrayInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SimpleArrayInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SimpleArrayInterfaceServiceFactory INSTANCE = createInstance(); + } + + private SimpleArrayInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SimpleArrayInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SimpleArrayInterfaceServiceFactory t = new SimpleArrayInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java new file mode 100644 index 0000000..5e329d5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class SimpleArrayInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SimpleArrayInterfaceStarter"; + + + + public static ISimpleArrayInterface start(Context context) { + stop(context); + androidService = new Intent(context, SimpleArrayInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SimpleArrayInterfaceServiceFactory factory = SimpleArrayInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SimpleArrayInterfaceServiceFactory"); + return SimpleArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java new file mode 100644 index 0000000..3bbbb89 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java @@ -0,0 +1,750 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_android_service.ISimpleInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SimpleInterfaceServiceAdapter extends Service +{ + private static final String TAG = "SimpleInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISimpleInterface mBackendService; + private static ISimpleInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public SimpleInterfaceServiceAdapter() + { + } + + public static ISimpleInterface setService(ISimpleInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SimpleInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SimpleInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISimpleInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SimpleInterfaceMessageType.fromInteger(msg.what) != SimpleInterfaceMessageType.REGISTER_CLIENT + && SimpleInterfaceMessageType.fromInteger(msg.what) != SimpleInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SimpleInterfaceMessageType" + SimpleInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SimpleInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + + boolean propBool = data.getBoolean("propBool", false); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + + int propInt = data.getInt("propInt", 0); + mBackendService.setPropInt(propInt); + break; + } + case PROP_PropInt32: + { + Bundle data = msg.getData(); + + int propInt32 = data.getInt("propInt32", 0); + mBackendService.setPropInt32(propInt32); + break; + } + case PROP_PropInt64: + { + Bundle data = msg.getData(); + + long propInt64 = data.getLong("propInt64", 0L); + mBackendService.setPropInt64(propInt64); + break; + } + case PROP_PropFloat: + { + Bundle data = msg.getData(); + + float propFloat = data.getFloat("propFloat", 0.0f); + mBackendService.setPropFloat(propFloat); + break; + } + case PROP_PropFloat32: + { + Bundle data = msg.getData(); + + float propFloat32 = data.getFloat("propFloat32", 0.0f); + mBackendService.setPropFloat32(propFloat32); + break; + } + case PROP_PropFloat64: + { + Bundle data = msg.getData(); + + double propFloat64 = data.getDouble("propFloat64", 0.0); + mBackendService.setPropFloat64(propFloat64); + break; + } + case PROP_PropString: + { + Bundle data = msg.getData(); + + String propString = data.getString("propString", new String()); + mBackendService.setPropString(propString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncNoReturnValueReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean paramBool = data.getBoolean("paramBool", false); + + mBackendService.funcNoReturnValue(paramBool); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncNoReturnValueResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + boolean paramBool = data.getBoolean("paramBool", false); + + boolean result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putBoolean("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncIntReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int paramInt = data.getInt("paramInt", 0); + + int result = mBackendService.funcInt(paramInt); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncIntResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncInt32Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int paramInt32 = data.getInt("paramInt32", 0); + + int result = mBackendService.funcInt32(paramInt32); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncInt32Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncInt64Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + long paramInt64 = data.getLong("paramInt64", 0L); + + long result = mBackendService.funcInt64(paramInt64); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncInt64Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putLong("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloatReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + float paramFloat = data.getFloat("paramFloat", 0.0f); + + float result = mBackendService.funcFloat(paramFloat); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncFloatResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putFloat("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloat32Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + float paramFloat32 = data.getFloat("paramFloat32", 0.0f); + + float result = mBackendService.funcFloat32(paramFloat32); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncFloat32Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putFloat("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloat64Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + double paramFloat = data.getDouble("paramFloat", 0.0); + + double result = mBackendService.funcFloat64(paramFloat); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncFloat64Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putDouble("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncStringReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + String paramString = data.getString("paramString", new String()); + + String result = mBackendService.funcString(paramString); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncStringResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putString("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + boolean propBool = mBackendService.getPropBool(); + + data.putBoolean("propBool", propBool); + int propInt = mBackendService.getPropInt(); + + data.putInt("propInt", propInt); + int propInt32 = mBackendService.getPropInt32(); + + data.putInt("propInt32", propInt32); + long propInt64 = mBackendService.getPropInt64(); + + data.putLong("propInt64", propInt64); + float propFloat = mBackendService.getPropFloat(); + + data.putFloat("propFloat", propFloat); + float propFloat32 = mBackendService.getPropFloat32(); + + data.putFloat("propFloat32", propFloat32); + double propFloat64 = mBackendService.getPropFloat64(); + + data.putDouble("propFloat64", propFloat64); + String propString = mBackendService.getPropString(); + + data.putString("propString", propString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(boolean propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("propBool", propBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(int propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt", propInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropInt32Changed(int propInt32){ + Log.i(TAG, "New value for PropInt32 from backend" + propInt32); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropInt32.getValue(); + Bundle data = new Bundle(); + + data.putInt("propInt32", propInt32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropInt64Changed(long propInt64){ + Log.i(TAG, "New value for PropInt64 from backend" + propInt64); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropInt64.getValue(); + Bundle data = new Bundle(); + + data.putLong("propInt64", propInt64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloatChanged(float propFloat){ + Log.i(TAG, "New value for PropFloat from backend" + propFloat); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putFloat("propFloat", propFloat); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloat32Changed(float propFloat32){ + Log.i(TAG, "New value for PropFloat32 from backend" + propFloat32); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropFloat32.getValue(); + Bundle data = new Bundle(); + + data.putFloat("propFloat32", propFloat32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloat64Changed(double propFloat64){ + Log.i(TAG, "New value for PropFloat64 from backend" + propFloat64); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropFloat64.getValue(); + Bundle data = new Bundle(); + + data.putDouble("propFloat64", propFloat64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropStringChanged(String propString){ + Log.i(TAG, "New value for PropString from backend" + propString); + + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SET_PropString.getValue(); + Bundle data = new Bundle(); + + data.putString("propString", propString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(boolean paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putBoolean("paramBool", paramBool); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt(int paramInt){ + Log.i(TAG, "New singal for SigInt = "+ " " + paramInt); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigInt.getValue(); + Bundle data = new Bundle(); + + data.putInt("paramInt", paramInt); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt32(int paramInt32){ + Log.i(TAG, "New singal for SigInt32 = "+ " " + paramInt32); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigInt32.getValue(); + Bundle data = new Bundle(); + + data.putInt("paramInt32", paramInt32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt64(long paramInt64){ + Log.i(TAG, "New singal for SigInt64 = "+ " " + paramInt64); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigInt64.getValue(); + Bundle data = new Bundle(); + + data.putLong("paramInt64", paramInt64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat(float paramFloat){ + Log.i(TAG, "New singal for SigFloat = "+ " " + paramFloat); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigFloat.getValue(); + Bundle data = new Bundle(); + + data.putFloat("paramFloat", paramFloat); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat32(float paramFloat32){ + Log.i(TAG, "New singal for SigFloat32 = "+ " " + paramFloat32); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigFloat32.getValue(); + Bundle data = new Bundle(); + + data.putFloat("paramFloat32", paramFloat32); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat64(double paramFloat64){ + Log.i(TAG, "New singal for SigFloat64 = "+ " " + paramFloat64); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigFloat64.getValue(); + Bundle data = new Bundle(); + + data.putDouble("paramFloat64", paramFloat64); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigString(String paramString){ + Log.i(TAG, "New singal for SigString = "+ " " + paramString); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.SIG_SigString.getValue(); + Bundle data = new Bundle(); + + data.putString("paramString", paramString); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java new file mode 100644 index 0000000..0cd2498 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.ISimpleInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SimpleInterfaceServiceFactory thread for the system. This is a thread for + * SimpleInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SimpleInterfaceServiceFactory extends HandlerThread implements ISimpleInterfaceServiceFactory +{ + private SimpleInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SimpleInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SimpleInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSimpleInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SimpleInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SimpleInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SimpleInterfaceServiceFactory INSTANCE = createInstance(); + } + + private SimpleInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SimpleInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SimpleInterfaceServiceFactory t = new SimpleInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java new file mode 100644 index 0000000..8c3a916 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.SimpleInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class SimpleInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SimpleInterfaceStarter"; + + + + public static ISimpleInterface start(Context context) { + stop(context); + androidService = new Intent(context, SimpleInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SimpleInterfaceServiceFactory factory = SimpleInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for SimpleInterfaceServiceFactory"); + return SimpleInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java new file mode 100644 index 0000000..0c213be --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java @@ -0,0 +1,261 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_android_service.IVoidInterfaceServiceFactory; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class VoidInterfaceServiceAdapter extends Service +{ + private static final String TAG = "VoidInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IVoidInterface mBackendService; + private static IVoidInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public VoidInterfaceServiceAdapter() + { + } + + public static IVoidInterface setService(IVoidInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(VoidInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: VoidInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(VoidInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(VoidInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IVoidInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (VoidInterfaceMessageType.fromInteger(msg.what) != VoidInterfaceMessageType.REGISTER_CLIENT + && VoidInterfaceMessageType.fromInteger(msg.what) != VoidInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: VoidInterfaceMessageType" + VoidInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (VoidInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncVoidReq: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + mBackendService.funcVoid(); + + Message respMsg = new Message(); + respMsg.what = VoidInterfaceMessageType.RPC_FuncVoidResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = VoidInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigVoid(){ + Log.i(TAG, "New singal for SigVoid = "); + Message msg = new Message(); + msg.what = VoidInterfaceMessageType.SIG_SigVoid.getValue(); + Bundle data = new Bundle(); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java new file mode 100644 index 0000000..4703f5e --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import tbSimple.tbSimple_android_service.IVoidInterfaceServiceFactory; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton VoidInterfaceServiceFactory thread for the system. This is a thread for + * VoidInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class VoidInterfaceServiceFactory extends HandlerThread implements IVoidInterfaceServiceFactory +{ + private VoidInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static VoidInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: VoidInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractVoidInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new VoidInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new VoidInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final VoidInterfaceServiceFactory INSTANCE = createInstance(); + } + + private VoidInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static VoidInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + VoidInterfaceServiceFactory t = new VoidInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java new file mode 100644 index 0000000..2834d9a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimple_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.VoidInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbSimple.tbSimple_impl; . +public class VoidInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "VoidInterfaceStarter"; + + + + public static IVoidInterface start(Context context) { + stop(context); + androidService = new Intent(context, VoidInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + VoidInterfaceServiceFactory factory = VoidInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for VoidInterfaceServiceFactory"); + return VoidInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..1dd1ab0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java @@ -0,0 +1,198 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_android_service.IEmptyInterfaceServiceFactory; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IEmptyInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class EmptyInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private EmptyInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IEmptyInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IEmptyInterface backendServiceMock = mock(IEmptyInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IEmptyInterfaceServiceFactory serviceFactory = mock(IEmptyInterfaceServiceFactory.class); + private IEmptyInterfaceMessageGetter clientMessagesStorage = mock(IEmptyInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, EmptyInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IEmptyInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + + + Message registerMsg = Message.obtain(null, EmptyInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EmptyInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, EmptyInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(EmptyInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IEmptyInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void onlySetupAndTeardown() + { + + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..2d48308 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java @@ -0,0 +1,305 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_android_service.INoOperationsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INoOperationsInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NoOperationsInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NoOperationsInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INoOperationsInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INoOperationsInterface backendServiceMock = mock(INoOperationsInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INoOperationsInterfaceServiceFactory serviceFactory = mock(INoOperationsInterfaceServiceFactory.class); + private INoOperationsInterfaceMessageGetter clientMessagesStorage = mock(INoOperationsInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NoOperationsInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INoOperationsInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + boolean initpropBool = true; + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + int initpropInt = 1; + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + + + Message registerMsg = Message.obtain(null, NoOperationsInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedpropBool = data.getBoolean("propBool", false); + + int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropBool, initpropBool); + assertEquals(receivedpropInt, initpropInt); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NoOperationsInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NoOperationsInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INoOperationsInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool(testpropBool); + + } + + @Test + public void whenNotifiedpropBool() + { + boolean testpropBool = true; + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + boolean receivedpropBool = data.getBoolean("propBool", false); + + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoOperationsInterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt(testpropInt); + + } + + @Test + public void whenNotifiedpropInt() + { + int testpropInt = 1; + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedpropInt = data.getInt("propInt", 0); + + assertEquals(receivedpropInt, testpropInt); + } + @Test + public void whenNotifiedsigVoid() + { + + testedAdapterAsEventListener.onSigVoid(); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.SIG_SigVoid.getValue(), response.what); + Bundle data = response.getData(); +} + @Test + public void whenNotifiedsigBool() + { + boolean testparamBool = true; + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoOperationsInterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); +} + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..a5b1c17 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java @@ -0,0 +1,280 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_android_service.INoPropertiesInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INoPropertiesInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NoPropertiesInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NoPropertiesInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INoPropertiesInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INoPropertiesInterface backendServiceMock = mock(INoPropertiesInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INoPropertiesInterfaceServiceFactory serviceFactory = mock(INoPropertiesInterfaceServiceFactory.class); + private INoPropertiesInterfaceMessageGetter clientMessagesStorage = mock(INoPropertiesInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NoPropertiesInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INoPropertiesInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + + + Message registerMsg = Message.obtain(null, NoPropertiesInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoPropertiesInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NoPropertiesInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NoPropertiesInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INoPropertiesInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void whenNotifiedsigVoid() + { + + testedAdapterAsEventListener.onSigVoid(); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoPropertiesInterfaceMessageType.SIG_SigVoid.getValue(), response.what); + Bundle data = response.getData(); +} + @Test + public void whenNotifiedsigBool() + { + boolean testparamBool = true; + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoPropertiesInterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); +} + + + public void onfuncVoidRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.RPC_FuncVoidReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcVoid(); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoPropertiesInterfaceMessageType.RPC_FuncVoidResp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoPropertiesInterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + boolean returnedValue = true; + + + when(backendServiceMock.funcBool(testparamBool)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool(testparamBool); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoPropertiesInterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + boolean receivedByClient = resp_data.getBoolean("result", false); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..265e6dd --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java @@ -0,0 +1,332 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_android_service.INoSignalsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INoSignalsInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NoSignalsInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NoSignalsInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INoSignalsInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INoSignalsInterface backendServiceMock = mock(INoSignalsInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INoSignalsInterfaceServiceFactory serviceFactory = mock(INoSignalsInterfaceServiceFactory.class); + private INoSignalsInterfaceMessageGetter clientMessagesStorage = mock(INoSignalsInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NoSignalsInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INoSignalsInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + boolean initpropBool = true; + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + int initpropInt = 1; + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + + + Message registerMsg = Message.obtain(null, NoSignalsInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedpropBool = data.getBoolean("propBool", false); + + int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropBool, initpropBool); + assertEquals(receivedpropInt, initpropInt); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NoSignalsInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NoSignalsInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INoSignalsInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool(testpropBool); + + } + + @Test + public void whenNotifiedpropBool() + { + boolean testpropBool = true; + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + boolean receivedpropBool = data.getBoolean("propBool", false); + + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt(testpropInt); + + } + + @Test + public void whenNotifiedpropInt() + { + int testpropInt = 1; + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedpropInt = data.getInt("propInt", 0); + + assertEquals(receivedpropInt, testpropInt); + } + + + public void onfuncVoidRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.RPC_FuncVoidReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcVoid(); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.RPC_FuncVoidResp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NoSignalsInterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + boolean returnedValue = true; + + + when(backendServiceMock.funcBool(testparamBool)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool(testparamBool); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NoSignalsInterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + boolean receivedByClient = resp_data.getBoolean("result", false); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..02ed697 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java @@ -0,0 +1,1018 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_android_service.ISimpleArrayInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISimpleArrayInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SimpleArrayInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SimpleArrayInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISimpleArrayInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISimpleArrayInterface backendServiceMock = mock(ISimpleArrayInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISimpleArrayInterfaceServiceFactory serviceFactory = mock(ISimpleArrayInterfaceServiceFactory.class); + private ISimpleArrayInterfaceMessageGetter clientMessagesStorage = mock(ISimpleArrayInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SimpleArrayInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISimpleArrayInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + boolean init_elementpropBool = true; + // todo fill if is struct + boolean[] initpropBool = new boolean[]{ init_elementpropBool } ; + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + int init_elementpropInt = 1; + // todo fill if is struct + int[] initpropInt = new int[]{ init_elementpropInt } ; + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + int init_elementpropInt32 = 1; + // todo fill if is struct + int[] initpropInt32 = new int[]{ init_elementpropInt32 } ; + when(backendServiceMock.getPropInt32()).thenReturn(initpropInt32); + long init_elementpropInt64 = 1L; + // todo fill if is struct + long[] initpropInt64 = new long[]{ init_elementpropInt64 } ; + when(backendServiceMock.getPropInt64()).thenReturn(initpropInt64); + float init_elementpropFloat = 1.0f; + // todo fill if is struct + float[] initpropFloat = new float[]{ init_elementpropFloat } ; + when(backendServiceMock.getPropFloat()).thenReturn(initpropFloat); + float init_elementpropFloat32 = 1.0f; + // todo fill if is struct + float[] initpropFloat32 = new float[]{ init_elementpropFloat32 } ; + when(backendServiceMock.getPropFloat32()).thenReturn(initpropFloat32); + double init_elementpropFloat64 = 1.0; + // todo fill if is struct + double[] initpropFloat64 = new double[]{ init_elementpropFloat64 } ; + when(backendServiceMock.getPropFloat64()).thenReturn(initpropFloat64); + String init_elementpropString = new String("xyz"); + // todo fill if is struct + String[] initpropString = new String[]{ init_elementpropString } ; + when(backendServiceMock.getPropString()).thenReturn(initpropString); + String initpropReadOnlyString = new String("xyz"); + when(backendServiceMock.getPropReadOnlyString()).thenReturn(initpropReadOnlyString); + + + Message registerMsg = Message.obtain(null, SimpleArrayInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt32(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt64(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat32(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat64(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropString(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropReadOnlyString(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + boolean[] receivedpropBool = data.getBooleanArray("propBool"); + + int[] receivedpropInt = data.getIntArray("propInt"); + + int[] receivedpropInt32 = data.getIntArray("propInt32"); + + long[] receivedpropInt64 = data.getLongArray("propInt64"); + + float[] receivedpropFloat = data.getFloatArray("propFloat"); + + float[] receivedpropFloat32 = data.getFloatArray("propFloat32"); + + double[] receivedpropFloat64 = data.getDoubleArray("propFloat64"); + + String[] receivedpropString = data.getStringArray("propString"); + + String receivedpropReadOnlyString = data.getString("propReadOnlyString", new String()); + assertEquals(receivedpropBool, initpropBool); + assertEquals(receivedpropInt, initpropInt); + assertEquals(receivedpropInt32, initpropInt32); + assertEquals(receivedpropInt64, initpropInt64); + assertEquals(receivedpropFloat, initpropFloat); + assertEquals(receivedpropFloat32, initpropFloat32); + assertEquals(receivedpropFloat64, initpropFloat64); + assertEquals(receivedpropString, initpropString); + assertEquals(receivedpropReadOnlyString, initpropReadOnlyString); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SimpleArrayInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SimpleArrayInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISimpleArrayInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + boolean[] testpropBool = new boolean[1]; + testpropBool[0] = true; + data.putBooleanArray("propBool", testpropBool); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool(testpropBool); + + } + + @Test + public void whenNotifiedpropBool() + { + boolean[] testpropBool = new boolean[1]; + testpropBool[0] = true; + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + boolean[] receivedpropBool = data.getBooleanArray("propBool"); + + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + int[] testpropInt = new int[1]; + testpropInt[0] = 1; + data.putIntArray("propInt", testpropInt); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt(testpropInt); + + } + + @Test + public void whenNotifiedpropInt() + { + int[] testpropInt = new int[1]; + testpropInt[0] = 1; + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + int[] receivedpropInt = data.getIntArray("propInt"); + + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropInt32.getValue()); + Bundle data = new Bundle(); + int[] testpropInt32 = new int[1]; + testpropInt32[0] = 1; + data.putIntArray("propInt32", testpropInt32); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt32(testpropInt32); + + } + + @Test + public void whenNotifiedpropInt32() + { + int[] testpropInt32 = new int[1]; + testpropInt32[0] = 1; + + testedAdapterAsEventListener.onPropInt32Changed(testpropInt32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropInt32.getValue(), response.what); + Bundle data = response.getData(); + + + int[] receivedpropInt32 = data.getIntArray("propInt32"); + + assertEquals(receivedpropInt32, testpropInt32); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropInt64.getValue()); + Bundle data = new Bundle(); + long[] testpropInt64 = new long[1]; + testpropInt64[0] = 1L; + data.putLongArray("propInt64", testpropInt64); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt64(testpropInt64); + + } + + @Test + public void whenNotifiedpropInt64() + { + long[] testpropInt64 = new long[1]; + testpropInt64[0] = 1L; + + testedAdapterAsEventListener.onPropInt64Changed(testpropInt64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropInt64.getValue(), response.what); + Bundle data = response.getData(); + + + long[] receivedpropInt64 = data.getLongArray("propInt64"); + + assertEquals(receivedpropInt64, testpropInt64); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropFloat.getValue()); + Bundle data = new Bundle(); + float[] testpropFloat = new float[1]; + testpropFloat[0] = 1.0f; + data.putFloatArray("propFloat", testpropFloat); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat(testpropFloat); + + } + + @Test + public void whenNotifiedpropFloat() + { + float[] testpropFloat = new float[1]; + testpropFloat[0] = 1.0f; + + testedAdapterAsEventListener.onPropFloatChanged(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + + float[] receivedpropFloat = data.getFloatArray("propFloat"); + + assertEquals(receivedpropFloat, testpropFloat); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropFloat32.getValue()); + Bundle data = new Bundle(); + float[] testpropFloat32 = new float[1]; + testpropFloat32[0] = 1.0f; + data.putFloatArray("propFloat32", testpropFloat32); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat32(testpropFloat32); + + } + + @Test + public void whenNotifiedpropFloat32() + { + float[] testpropFloat32 = new float[1]; + testpropFloat32[0] = 1.0f; + + testedAdapterAsEventListener.onPropFloat32Changed(testpropFloat32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropFloat32.getValue(), response.what); + Bundle data = response.getData(); + + + float[] receivedpropFloat32 = data.getFloatArray("propFloat32"); + + assertEquals(receivedpropFloat32, testpropFloat32); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropFloat64.getValue()); + Bundle data = new Bundle(); + double[] testpropFloat64 = new double[1]; + testpropFloat64[0] = 1.0; + data.putDoubleArray("propFloat64", testpropFloat64); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat64(testpropFloat64); + + } + + @Test + public void whenNotifiedpropFloat64() + { + double[] testpropFloat64 = new double[1]; + testpropFloat64[0] = 1.0; + + testedAdapterAsEventListener.onPropFloat64Changed(testpropFloat64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropFloat64.getValue(), response.what); + Bundle data = response.getData(); + + + double[] receivedpropFloat64 = data.getDoubleArray("propFloat64"); + + assertEquals(receivedpropFloat64, testpropFloat64); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropString.getValue()); + Bundle data = new Bundle(); + String[] testpropString = new String[1]; + testpropString[0] = new String("xyz"); + data.putStringArray("propString", testpropString); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropString(testpropString); + + } + + @Test + public void whenNotifiedpropString() + { + String[] testpropString = new String[1]; + testpropString[0] = new String("xyz"); + + testedAdapterAsEventListener.onPropStringChanged(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropString.getValue(), response.what); + Bundle data = response.getData(); + + + String[] receivedpropString = data.getStringArray("propString"); + + assertEquals(receivedpropString, testpropString); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropReadOnlyStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropReadOnlyString.getValue()); + Bundle data = new Bundle(); + String testpropReadOnlyString = new String("xyz"); + data.putString("propReadOnlyString", testpropReadOnlyString); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropReadOnlyString(testpropReadOnlyString); + + } + + @Test + public void whenNotifiedpropReadOnlyString() + { + String testpropReadOnlyString = new String("xyz"); + + testedAdapterAsEventListener.onPropReadOnlyStringChanged(testpropReadOnlyString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SET_PropReadOnlyString.getValue(), response.what); + Bundle data = response.getData(); + + + String receivedpropReadOnlyString = data.getString("propReadOnlyString", new String()); + + assertEquals(receivedpropReadOnlyString, testpropReadOnlyString); + } + @Test + public void whenNotifiedsigBool() + { + boolean[] testparamBool = new boolean[1]; + testparamBool[0] = true; + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean[] receivedparamBool = data.getBooleanArray("paramBool"); + assertEquals(receivedparamBool, testparamBool); +} + @Test + public void whenNotifiedsigInt() + { + int[] testparamInt = new int[1]; + testparamInt[0] = 1; + + testedAdapterAsEventListener.onSigInt(testparamInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigInt.getValue(), response.what); + Bundle data = response.getData(); + + int[] receivedparamInt = data.getIntArray("paramInt"); + assertEquals(receivedparamInt, testparamInt); +} + @Test + public void whenNotifiedsigInt32() + { + int[] testparamInt32 = new int[1]; + testparamInt32[0] = 1; + + testedAdapterAsEventListener.onSigInt32(testparamInt32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigInt32.getValue(), response.what); + Bundle data = response.getData(); + + int[] receivedparamInt32 = data.getIntArray("paramInt32"); + assertEquals(receivedparamInt32, testparamInt32); +} + @Test + public void whenNotifiedsigInt64() + { + long[] testparamInt64 = new long[1]; + testparamInt64[0] = 1L; + + testedAdapterAsEventListener.onSigInt64(testparamInt64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigInt64.getValue(), response.what); + Bundle data = response.getData(); + + long[] receivedparamInt64 = data.getLongArray("paramInt64"); + assertEquals(receivedparamInt64, testparamInt64); +} + @Test + public void whenNotifiedsigFloat() + { + float[] testparamFloat = new float[1]; + testparamFloat[0] = 1.0f; + + testedAdapterAsEventListener.onSigFloat(testparamFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigFloat.getValue(), response.what); + Bundle data = response.getData(); + + float[] receivedparamFloat = data.getFloatArray("paramFloat"); + assertEquals(receivedparamFloat, testparamFloat); +} + @Test + public void whenNotifiedsigFloat32() + { + float[] testparamFloa32 = new float[1]; + testparamFloa32[0] = 1.0f; + + testedAdapterAsEventListener.onSigFloat32(testparamFloa32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigFloat32.getValue(), response.what); + Bundle data = response.getData(); + + float[] receivedparamFloa32 = data.getFloatArray("paramFloa32"); + assertEquals(receivedparamFloa32, testparamFloa32); +} + @Test + public void whenNotifiedsigFloat64() + { + double[] testparamFloat64 = new double[1]; + testparamFloat64[0] = 1.0; + + testedAdapterAsEventListener.onSigFloat64(testparamFloat64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigFloat64.getValue(), response.what); + Bundle data = response.getData(); + + double[] receivedparamFloat64 = data.getDoubleArray("paramFloat64"); + assertEquals(receivedparamFloat64, testparamFloat64); +} + @Test + public void whenNotifiedsigString() + { + String[] testparamString = new String[1]; + testparamString[0] = new String("xyz"); + + testedAdapterAsEventListener.onSigString(testparamString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.SIG_SigString.getValue(), response.what); + Bundle data = response.getData(); + + String[] receivedparamString = data.getStringArray("paramString"); + assertEquals(receivedparamString, testparamString); +} + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean[] testparamBool = new boolean[1]; + testparamBool[0] = true; + data.putBooleanArray("paramBool", testparamBool); + boolean[] returnedValue = new boolean[1]; + returnedValue[0] = true; + + + when(backendServiceMock.funcBool(testparamBool)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool(testparamBool); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + boolean[] receivedByClient = resp_data.getBooleanArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncIntRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncIntReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int[] testparamInt = new int[1]; + testparamInt[0] = 1; + data.putIntArray("paramInt", testparamInt); + int[] returnedValue = new int[1]; + returnedValue[0] = 1; + + + when(backendServiceMock.funcInt(testparamInt)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt(testparamInt); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncIntResp.getValue(), response.what); + Bundle resp_data = response.getData(); + int[] receivedByClient = resp_data.getIntArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncInt32Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncInt32Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int[] testparamInt32 = new int[1]; + testparamInt32[0] = 1; + data.putIntArray("paramInt32", testparamInt32); + int[] returnedValue = new int[1]; + returnedValue[0] = 1; + + + when(backendServiceMock.funcInt32(testparamInt32)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt32(testparamInt32); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncInt32Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + int[] receivedByClient = resp_data.getIntArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncInt64Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncInt64Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + long[] testparamInt64 = new long[1]; + testparamInt64[0] = 1L; + data.putLongArray("paramInt64", testparamInt64); + long[] returnedValue = new long[1]; + returnedValue[0] = 1L; + + + when(backendServiceMock.funcInt64(testparamInt64)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt64(testparamInt64); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncInt64Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + long[] receivedByClient = resp_data.getLongArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloatRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncFloatReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + float[] testparamFloat = new float[1]; + testparamFloat[0] = 1.0f; + data.putFloatArray("paramFloat", testparamFloat); + float[] returnedValue = new float[1]; + returnedValue[0] = 1.0f; + + + when(backendServiceMock.funcFloat(testparamFloat)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat(testparamFloat); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloatResp.getValue(), response.what); + Bundle resp_data = response.getData(); + float[] receivedByClient = resp_data.getFloatArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloat32Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncFloat32Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + float[] testparamFloat32 = new float[1]; + testparamFloat32[0] = 1.0f; + data.putFloatArray("paramFloat32", testparamFloat32); + float[] returnedValue = new float[1]; + returnedValue[0] = 1.0f; + + + when(backendServiceMock.funcFloat32(testparamFloat32)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat32(testparamFloat32); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloat32Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + float[] receivedByClient = resp_data.getFloatArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloat64Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncFloat64Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + double[] testparamFloat = new double[1]; + testparamFloat[0] = 1.0; + data.putDoubleArray("paramFloat", testparamFloat); + double[] returnedValue = new double[1]; + returnedValue[0] = 1.0; + + + when(backendServiceMock.funcFloat64(testparamFloat)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat64(testparamFloat); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloat64Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + double[] receivedByClient = resp_data.getDoubleArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncStringRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.RPC_FuncStringReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + String[] testparamString = new String[1]; + testparamString[0] = new String("xyz"); + data.putStringArray("paramString", testparamString); + String[] returnedValue = new String[1]; + returnedValue[0] = new String("xyz"); + + + when(backendServiceMock.funcString(testparamString)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcString(testparamString); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncStringResp.getValue(), response.what); + Bundle resp_data = response.getData(); + String[] receivedByClient = resp_data.getStringArray("result"); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..161eaa7 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java @@ -0,0 +1,959 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_android_service.ISimpleInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISimpleInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SimpleInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SimpleInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISimpleInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISimpleInterface backendServiceMock = mock(ISimpleInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISimpleInterfaceServiceFactory serviceFactory = mock(ISimpleInterfaceServiceFactory.class); + private ISimpleInterfaceMessageGetter clientMessagesStorage = mock(ISimpleInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SimpleInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISimpleInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + boolean initpropBool = true; + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + int initpropInt = 1; + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + int initpropInt32 = 1; + when(backendServiceMock.getPropInt32()).thenReturn(initpropInt32); + long initpropInt64 = 1L; + when(backendServiceMock.getPropInt64()).thenReturn(initpropInt64); + float initpropFloat = 1.0f; + when(backendServiceMock.getPropFloat()).thenReturn(initpropFloat); + float initpropFloat32 = 1.0f; + when(backendServiceMock.getPropFloat32()).thenReturn(initpropFloat32); + double initpropFloat64 = 1.0; + when(backendServiceMock.getPropFloat64()).thenReturn(initpropFloat64); + String initpropString = new String("xyz"); + when(backendServiceMock.getPropString()).thenReturn(initpropString); + + + Message registerMsg = Message.obtain(null, SimpleInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt32(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt64(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat32(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat64(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropString(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedpropBool = data.getBoolean("propBool", false); + + int receivedpropInt = data.getInt("propInt", 0); + + int receivedpropInt32 = data.getInt("propInt32", 0); + + long receivedpropInt64 = data.getLong("propInt64", 0L); + + float receivedpropFloat = data.getFloat("propFloat", 0.0f); + + float receivedpropFloat32 = data.getFloat("propFloat32", 0.0f); + + double receivedpropFloat64 = data.getDouble("propFloat64", 0.0); + + String receivedpropString = data.getString("propString", new String()); + assertEquals(receivedpropBool, initpropBool); + assertEquals(receivedpropInt, initpropInt); + assertEquals(receivedpropInt32, initpropInt32); + assertEquals(receivedpropInt64, initpropInt64); + assertEquals(receivedpropFloat, initpropFloat, + 1e-6f); + assertEquals(receivedpropFloat32, initpropFloat32, + 1e-6f); + assertEquals(receivedpropFloat64, initpropFloat64, + 1e-6f); + assertEquals(receivedpropString, initpropString); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SimpleInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SimpleInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISimpleInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + boolean testpropBool = true; + data.putBoolean("propBool", testpropBool); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool(testpropBool); + + } + + @Test + public void whenNotifiedpropBool() + { + boolean testpropBool = true; + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + boolean receivedpropBool = data.getBoolean("propBool", false); + + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + int testpropInt = 1; + data.putInt("propInt", testpropInt); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt(testpropInt); + + } + + @Test + public void whenNotifiedpropInt() + { + int testpropInt = 1; + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedpropInt = data.getInt("propInt", 0); + + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropInt32.getValue()); + Bundle data = new Bundle(); + int testpropInt32 = 1; + data.putInt("propInt32", testpropInt32); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt32(testpropInt32); + + } + + @Test + public void whenNotifiedpropInt32() + { + int testpropInt32 = 1; + + testedAdapterAsEventListener.onPropInt32Changed(testpropInt32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropInt32.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedpropInt32 = data.getInt("propInt32", 0); + + assertEquals(receivedpropInt32, testpropInt32); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropInt64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropInt64.getValue()); + Bundle data = new Bundle(); + long testpropInt64 = 1L; + data.putLong("propInt64", testpropInt64); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt64(testpropInt64); + + } + + @Test + public void whenNotifiedpropInt64() + { + long testpropInt64 = 1L; + + testedAdapterAsEventListener.onPropInt64Changed(testpropInt64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropInt64.getValue(), response.what); + Bundle data = response.getData(); + + + long receivedpropInt64 = data.getLong("propInt64", 0L); + + assertEquals(receivedpropInt64, testpropInt64); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropFloat.getValue()); + Bundle data = new Bundle(); + float testpropFloat = 1.0f; + data.putFloat("propFloat", testpropFloat); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat(testpropFloat); + + } + + @Test + public void whenNotifiedpropFloat() + { + float testpropFloat = 1.0f; + + testedAdapterAsEventListener.onPropFloatChanged(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + + float receivedpropFloat = data.getFloat("propFloat", 0.0f); + + assertEquals(receivedpropFloat, testpropFloat, + 1e-6f); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropFloat32.getValue()); + Bundle data = new Bundle(); + float testpropFloat32 = 1.0f; + data.putFloat("propFloat32", testpropFloat32); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat32(testpropFloat32); + + } + + @Test + public void whenNotifiedpropFloat32() + { + float testpropFloat32 = 1.0f; + + testedAdapterAsEventListener.onPropFloat32Changed(testpropFloat32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropFloat32.getValue(), response.what); + Bundle data = response.getData(); + + + float receivedpropFloat32 = data.getFloat("propFloat32", 0.0f); + + assertEquals(receivedpropFloat32, testpropFloat32, + 1e-6f); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropFloat64.getValue()); + Bundle data = new Bundle(); + double testpropFloat64 = 1.0; + data.putDouble("propFloat64", testpropFloat64); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat64(testpropFloat64); + + } + + @Test + public void whenNotifiedpropFloat64() + { + double testpropFloat64 = 1.0; + + testedAdapterAsEventListener.onPropFloat64Changed(testpropFloat64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropFloat64.getValue(), response.what); + Bundle data = response.getData(); + + + double receivedpropFloat64 = data.getDouble("propFloat64", 0.0); + + assertEquals(receivedpropFloat64, testpropFloat64, + 1e-6f); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.PROP_PropString.getValue()); + Bundle data = new Bundle(); + String testpropString = new String("xyz"); + data.putString("propString", testpropString); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropString(testpropString); + + } + + @Test + public void whenNotifiedpropString() + { + String testpropString = new String("xyz"); + + testedAdapterAsEventListener.onPropStringChanged(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SET_PropString.getValue(), response.what); + Bundle data = response.getData(); + + + String receivedpropString = data.getString("propString", new String()); + + assertEquals(receivedpropString, testpropString); + } + @Test + public void whenNotifiedsigBool() + { + boolean testparamBool = true; + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + + boolean receivedparamBool = data.getBoolean("paramBool", false); + assertEquals(receivedparamBool, testparamBool); +} + @Test + public void whenNotifiedsigInt() + { + int testparamInt = 1; + + testedAdapterAsEventListener.onSigInt(testparamInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigInt.getValue(), response.what); + Bundle data = response.getData(); + + int receivedparamInt = data.getInt("paramInt", 0); + assertEquals(receivedparamInt, testparamInt); +} + @Test + public void whenNotifiedsigInt32() + { + int testparamInt32 = 1; + + testedAdapterAsEventListener.onSigInt32(testparamInt32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigInt32.getValue(), response.what); + Bundle data = response.getData(); + + int receivedparamInt32 = data.getInt("paramInt32", 0); + assertEquals(receivedparamInt32, testparamInt32); +} + @Test + public void whenNotifiedsigInt64() + { + long testparamInt64 = 1L; + + testedAdapterAsEventListener.onSigInt64(testparamInt64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigInt64.getValue(), response.what); + Bundle data = response.getData(); + + long receivedparamInt64 = data.getLong("paramInt64", 0L); + assertEquals(receivedparamInt64, testparamInt64); +} + @Test + public void whenNotifiedsigFloat() + { + float testparamFloat = 1.0f; + + testedAdapterAsEventListener.onSigFloat(testparamFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigFloat.getValue(), response.what); + Bundle data = response.getData(); + + float receivedparamFloat = data.getFloat("paramFloat", 0.0f); + assertEquals(receivedparamFloat, testparamFloat, + 1e-6f); +} + @Test + public void whenNotifiedsigFloat32() + { + float testparamFloat32 = 1.0f; + + testedAdapterAsEventListener.onSigFloat32(testparamFloat32); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigFloat32.getValue(), response.what); + Bundle data = response.getData(); + + float receivedparamFloat32 = data.getFloat("paramFloat32", 0.0f); + assertEquals(receivedparamFloat32, testparamFloat32, + 1e-6f); +} + @Test + public void whenNotifiedsigFloat64() + { + double testparamFloat64 = 1.0; + + testedAdapterAsEventListener.onSigFloat64(testparamFloat64); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigFloat64.getValue(), response.what); + Bundle data = response.getData(); + + double receivedparamFloat64 = data.getDouble("paramFloat64", 0.0); + assertEquals(receivedparamFloat64, testparamFloat64, + 1e-6f); +} + @Test + public void whenNotifiedsigString() + { + String testparamString = new String("xyz"); + + testedAdapterAsEventListener.onSigString(testparamString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.SIG_SigString.getValue(), response.what); + Bundle data = response.getData(); + + String receivedparamString = data.getString("paramString", new String()); + assertEquals(receivedparamString, testparamString); +} + + + public void onfuncNoReturnValueRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncNoReturnValueReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcNoReturnValue(testparamBool); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncNoReturnValueResp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean testparamBool = true; + data.putBoolean("paramBool", testparamBool); + boolean returnedValue = true; + + + when(backendServiceMock.funcBool(testparamBool)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool(testparamBool); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + boolean receivedByClient = resp_data.getBoolean("result", false); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncIntRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncIntReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparamInt = 1; + data.putInt("paramInt", testparamInt); + int returnedValue = 1; + + + when(backendServiceMock.funcInt(testparamInt)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt(testparamInt); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncIntResp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncInt32Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncInt32Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparamInt32 = 1; + data.putInt("paramInt32", testparamInt32); + int returnedValue = 1; + + + when(backendServiceMock.funcInt32(testparamInt32)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt32(testparamInt32); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncInt32Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncInt64Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncInt64Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + long testparamInt64 = 1L; + data.putLong("paramInt64", testparamInt64); + long returnedValue = 1L; + + + when(backendServiceMock.funcInt64(testparamInt64)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt64(testparamInt64); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncInt64Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + long receivedByClient = resp_data.getLong("result", 0L); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloatRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncFloatReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + float testparamFloat = 1.0f; + data.putFloat("paramFloat", testparamFloat); + float returnedValue = 1.0f; + + + when(backendServiceMock.funcFloat(testparamFloat)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat(testparamFloat); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncFloatResp.getValue(), response.what); + Bundle resp_data = response.getData(); + float receivedByClient = resp_data.getFloat("result", 0.0f); + + assertEquals(receivedByClient, returnedValue, + 1e-6f); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloat32Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncFloat32Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + float testparamFloat32 = 1.0f; + data.putFloat("paramFloat32", testparamFloat32); + float returnedValue = 1.0f; + + + when(backendServiceMock.funcFloat32(testparamFloat32)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat32(testparamFloat32); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncFloat32Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + float receivedByClient = resp_data.getFloat("result", 0.0f); + + assertEquals(receivedByClient, returnedValue, + 1e-6f); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloat64Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncFloat64Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + double testparamFloat = 1.0; + data.putDouble("paramFloat", testparamFloat); + double returnedValue = 1.0; + + + when(backendServiceMock.funcFloat64(testparamFloat)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat64(testparamFloat); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncFloat64Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + double receivedByClient = resp_data.getDouble("result", 0.0); + + assertEquals(receivedByClient, returnedValue, + 1e-6f); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncStringRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncStringReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + String testparamString = new String("xyz"); + data.putString("paramString", testparamString); + String returnedValue = new String("xyz"); + + + when(backendServiceMock.funcString(testparamString)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcString(testparamString); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncStringResp.getValue(), response.what); + Bundle resp_data = response.getData(); + String receivedByClient = resp_data.getString("result", new String()); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..3d7a6c5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java @@ -0,0 +1,230 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimple_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter; + +//import message type and parcelabe types +import tbSimple.tbSimple_api.TbSimpleTestHelper; + + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_android_service.IVoidInterfaceServiceFactory; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IVoidInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class VoidInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private VoidInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IVoidInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IVoidInterface backendServiceMock = mock(IVoidInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IVoidInterfaceServiceFactory serviceFactory = mock(IVoidInterfaceServiceFactory.class); + private IVoidInterfaceMessageGetter clientMessagesStorage = mock(IVoidInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, VoidInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IVoidInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + + + Message registerMsg = Message.obtain(null, VoidInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(VoidInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, VoidInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(VoidInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IVoidInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void whenNotifiedsigVoid() + { + + testedAdapterAsEventListener.onSigVoid(); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(VoidInterfaceMessageType.SIG_SigVoid.getValue(), response.what); + Bundle data = response.getData(); +} + + + public void onfuncVoidRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, VoidInterfaceMessageType.RPC_FuncVoidReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcVoid(); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(VoidInterfaceMessageType.RPC_FuncVoidResp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_api/additions.gradle b/goldenmaster/tbSimple/tbSimple_api/additions.gradle new file mode 100644 index 0000000..07e9130 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'tbSimple.tbSimple_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_api/build.gradle b/goldenmaster/tbSimple/tbSimple_api/build.gradle new file mode 100644 index 0000000..10f1099 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "tbSimple" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java new file mode 100644 index 0000000..aa11f79 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java @@ -0,0 +1,25 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_api.IEmptyInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractEmptyInterface implements IEmptyInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IEmptyInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IEmptyInterfaceEventListener listener) { + listeners.remove(listener); + } + + public void fire_readyStatusChanged(boolean isReady) + { + for (IEmptyInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java new file mode 100644 index 0000000..bf330cc --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java @@ -0,0 +1,53 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_api.INoOperationsInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNoOperationsInterface implements INoOperationsInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INoOperationsInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INoOperationsInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(boolean newValue) { + for (INoOperationsInterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(int newValue) { + for (INoOperationsInterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + @Override + public void fireSigVoid() { + for (INoOperationsInterfaceEventListener listener : listeners) { + listener.onSigVoid(); + } + } + + @Override + public void fireSigBool(boolean paramBool) { + for (INoOperationsInterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INoOperationsInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java new file mode 100644 index 0000000..306aaef --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java @@ -0,0 +1,39 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_api.INoPropertiesInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNoPropertiesInterface implements INoPropertiesInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INoPropertiesInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INoPropertiesInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireSigVoid() { + for (INoPropertiesInterfaceEventListener listener : listeners) { + listener.onSigVoid(); + } + } + + @Override + public void fireSigBool(boolean paramBool) { + for (INoPropertiesInterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INoPropertiesInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java new file mode 100644 index 0000000..3ef0003 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java @@ -0,0 +1,39 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_api.INoSignalsInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNoSignalsInterface implements INoSignalsInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INoSignalsInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INoSignalsInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(boolean newValue) { + for (INoSignalsInterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(int newValue) { + for (INoSignalsInterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INoSignalsInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java new file mode 100644 index 0000000..bd60111 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java @@ -0,0 +1,144 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSimpleArrayInterface implements ISimpleArrayInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISimpleArrayInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISimpleArrayInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(boolean[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(int[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + @Override + public void firePropInt32Changed(int[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropInt32Changed(newValue); + } + } + + @Override + public void firePropInt64Changed(long[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropInt64Changed(newValue); + } + } + + @Override + public void firePropFloatChanged(float[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropFloatChanged(newValue); + } + } + + @Override + public void firePropFloat32Changed(float[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropFloat32Changed(newValue); + } + } + + @Override + public void firePropFloat64Changed(double[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropFloat64Changed(newValue); + } + } + + @Override + public void firePropStringChanged(String[] newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropStringChanged(newValue); + } + } + + @Override + public void firePropReadOnlyStringChanged(String newValue) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onPropReadOnlyStringChanged(newValue); + } + } + + @Override + public void fireSigBool(boolean[] paramBool) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + @Override + public void fireSigInt(int[] paramInt) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigInt(paramInt); + } + } + + @Override + public void fireSigInt32(int[] paramInt32) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigInt32(paramInt32); + } + } + + @Override + public void fireSigInt64(long[] paramInt64) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigInt64(paramInt64); + } + } + + @Override + public void fireSigFloat(float[] paramFloat) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigFloat(paramFloat); + } + } + + @Override + public void fireSigFloat32(float[] paramFloa32) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigFloat32(paramFloa32); + } + } + + @Override + public void fireSigFloat64(double[] paramFloat64) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigFloat64(paramFloat64); + } + } + + @Override + public void fireSigString(String[] paramString) { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.onSigString(paramString); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISimpleArrayInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java new file mode 100644 index 0000000..7102f7b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java @@ -0,0 +1,137 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSimpleInterface implements ISimpleInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISimpleInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISimpleInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(boolean newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(int newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + @Override + public void firePropInt32Changed(int newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropInt32Changed(newValue); + } + } + + @Override + public void firePropInt64Changed(long newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropInt64Changed(newValue); + } + } + + @Override + public void firePropFloatChanged(float newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropFloatChanged(newValue); + } + } + + @Override + public void firePropFloat32Changed(float newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropFloat32Changed(newValue); + } + } + + @Override + public void firePropFloat64Changed(double newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropFloat64Changed(newValue); + } + } + + @Override + public void firePropStringChanged(String newValue) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onPropStringChanged(newValue); + } + } + + @Override + public void fireSigBool(boolean paramBool) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + @Override + public void fireSigInt(int paramInt) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigInt(paramInt); + } + } + + @Override + public void fireSigInt32(int paramInt32) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigInt32(paramInt32); + } + } + + @Override + public void fireSigInt64(long paramInt64) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigInt64(paramInt64); + } + } + + @Override + public void fireSigFloat(float paramFloat) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigFloat(paramFloat); + } + } + + @Override + public void fireSigFloat32(float paramFloat32) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigFloat32(paramFloat32); + } + } + + @Override + public void fireSigFloat64(double paramFloat64) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigFloat64(paramFloat64); + } + } + + @Override + public void fireSigString(String paramString) { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.onSigString(paramString); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISimpleInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java new file mode 100644 index 0000000..4e471a2 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java @@ -0,0 +1,32 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; +//TODO imported/extern modules + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractVoidInterface implements IVoidInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IVoidInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IVoidInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireSigVoid() { + for (IVoidInterfaceEventListener listener : listeners) { + listener.onSigVoid(); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IVoidInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterface.java new file mode 100644 index 0000000..edbcb5a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterface.java @@ -0,0 +1,16 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface IEmptyInterface { + // properties + // methods + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IEmptyInterfaceEventListener listener); + void removeEventListener(IEmptyInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterfaceEventListener.java new file mode 100644 index 0000000..8c98e74 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IEmptyInterfaceEventListener.java @@ -0,0 +1,5 @@ +package tbSimple.tbSimple_api; + + public interface IEmptyInterfaceEventListener { + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterface.java new file mode 100644 index 0000000..3cc1fd0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterface.java @@ -0,0 +1,26 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface INoOperationsInterface { + // properties + void setPropBool(boolean propBool); + boolean getPropBool(); + void firePropBoolChanged(boolean newValue); + + void setPropInt(int propInt); + int getPropInt(); + void firePropIntChanged(int newValue); + + // methods + public void fireSigVoid(); + public void fireSigBool(boolean paramBool); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INoOperationsInterfaceEventListener listener); + void removeEventListener(INoOperationsInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterfaceEventListener.java new file mode 100644 index 0000000..d1b43f2 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoOperationsInterfaceEventListener.java @@ -0,0 +1,9 @@ +package tbSimple.tbSimple_api; + + public interface INoOperationsInterfaceEventListener { + void onPropBoolChanged(boolean newValue); + void onPropIntChanged(int newValue); + void onSigVoid(); + void onSigBool(boolean paramBool); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterface.java new file mode 100644 index 0000000..2797a4d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterface.java @@ -0,0 +1,22 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface INoPropertiesInterface { + // properties + // methods + void funcVoid(); + CompletableFuture funcVoidAsync(); + boolean funcBool(boolean paramBool); + CompletableFuture funcBoolAsync(boolean paramBool); + public void fireSigVoid(); + public void fireSigBool(boolean paramBool); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INoPropertiesInterfaceEventListener listener); + void removeEventListener(INoPropertiesInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterfaceEventListener.java new file mode 100644 index 0000000..e36727c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoPropertiesInterfaceEventListener.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_api; + + public interface INoPropertiesInterfaceEventListener { + void onSigVoid(); + void onSigBool(boolean paramBool); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterface.java new file mode 100644 index 0000000..18839c0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterface.java @@ -0,0 +1,28 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface INoSignalsInterface { + // properties + void setPropBool(boolean propBool); + boolean getPropBool(); + void firePropBoolChanged(boolean newValue); + + void setPropInt(int propInt); + int getPropInt(); + void firePropIntChanged(int newValue); + + // methods + void funcVoid(); + CompletableFuture funcVoidAsync(); + boolean funcBool(boolean paramBool); + CompletableFuture funcBoolAsync(boolean paramBool); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INoSignalsInterfaceEventListener listener); + void removeEventListener(INoSignalsInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterfaceEventListener.java new file mode 100644 index 0000000..ab6f229 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/INoSignalsInterfaceEventListener.java @@ -0,0 +1,7 @@ +package tbSimple.tbSimple_api; + + public interface INoSignalsInterfaceEventListener { + void onPropBoolChanged(boolean newValue); + void onPropIntChanged(int newValue); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterface.java new file mode 100644 index 0000000..ca39e7b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterface.java @@ -0,0 +1,76 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface ISimpleArrayInterface { + // properties + void setPropBool(boolean[] propBool); + boolean[] getPropBool(); + void firePropBoolChanged(boolean[] newValue); + + void setPropInt(int[] propInt); + int[] getPropInt(); + void firePropIntChanged(int[] newValue); + + void setPropInt32(int[] propInt32); + int[] getPropInt32(); + void firePropInt32Changed(int[] newValue); + + void setPropInt64(long[] propInt64); + long[] getPropInt64(); + void firePropInt64Changed(long[] newValue); + + void setPropFloat(float[] propFloat); + float[] getPropFloat(); + void firePropFloatChanged(float[] newValue); + + void setPropFloat32(float[] propFloat32); + float[] getPropFloat32(); + void firePropFloat32Changed(float[] newValue); + + void setPropFloat64(double[] propFloat64); + double[] getPropFloat64(); + void firePropFloat64Changed(double[] newValue); + + void setPropString(String[] propString); + String[] getPropString(); + void firePropStringChanged(String[] newValue); + + void setPropReadOnlyString(String propReadOnlyString); + String getPropReadOnlyString(); + void firePropReadOnlyStringChanged(String newValue); + + // methods + boolean[] funcBool(boolean[] paramBool); + CompletableFuture funcBoolAsync(boolean[] paramBool); + int[] funcInt(int[] paramInt); + CompletableFuture funcIntAsync(int[] paramInt); + int[] funcInt32(int[] paramInt32); + CompletableFuture funcInt32Async(int[] paramInt32); + long[] funcInt64(long[] paramInt64); + CompletableFuture funcInt64Async(long[] paramInt64); + float[] funcFloat(float[] paramFloat); + CompletableFuture funcFloatAsync(float[] paramFloat); + float[] funcFloat32(float[] paramFloat32); + CompletableFuture funcFloat32Async(float[] paramFloat32); + double[] funcFloat64(double[] paramFloat); + CompletableFuture funcFloat64Async(double[] paramFloat); + String[] funcString(String[] paramString); + CompletableFuture funcStringAsync(String[] paramString); + public void fireSigBool(boolean[] paramBool); + public void fireSigInt(int[] paramInt); + public void fireSigInt32(int[] paramInt32); + public void fireSigInt64(long[] paramInt64); + public void fireSigFloat(float[] paramFloat); + public void fireSigFloat32(float[] paramFloa32); + public void fireSigFloat64(double[] paramFloat64); + public void fireSigString(String[] paramString); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISimpleArrayInterfaceEventListener listener); + void removeEventListener(ISimpleArrayInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterfaceEventListener.java new file mode 100644 index 0000000..76240bd --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleArrayInterfaceEventListener.java @@ -0,0 +1,22 @@ +package tbSimple.tbSimple_api; + + public interface ISimpleArrayInterfaceEventListener { + void onPropBoolChanged(boolean[] newValue); + void onPropIntChanged(int[] newValue); + void onPropInt32Changed(int[] newValue); + void onPropInt64Changed(long[] newValue); + void onPropFloatChanged(float[] newValue); + void onPropFloat32Changed(float[] newValue); + void onPropFloat64Changed(double[] newValue); + void onPropStringChanged(String[] newValue); + void onPropReadOnlyStringChanged(String newValue); + void onSigBool(boolean[] paramBool); + void onSigInt(int[] paramInt); + void onSigInt32(int[] paramInt32); + void onSigInt64(long[] paramInt64); + void onSigFloat(float[] paramFloat); + void onSigFloat32(float[] paramFloa32); + void onSigFloat64(double[] paramFloat64); + void onSigString(String[] paramString); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java new file mode 100644 index 0000000..b02897b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java @@ -0,0 +1,74 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface ISimpleInterface { + // properties + void setPropBool(boolean propBool); + boolean getPropBool(); + void firePropBoolChanged(boolean newValue); + + void setPropInt(int propInt); + int getPropInt(); + void firePropIntChanged(int newValue); + + void setPropInt32(int propInt32); + int getPropInt32(); + void firePropInt32Changed(int newValue); + + void setPropInt64(long propInt64); + long getPropInt64(); + void firePropInt64Changed(long newValue); + + void setPropFloat(float propFloat); + float getPropFloat(); + void firePropFloatChanged(float newValue); + + void setPropFloat32(float propFloat32); + float getPropFloat32(); + void firePropFloat32Changed(float newValue); + + void setPropFloat64(double propFloat64); + double getPropFloat64(); + void firePropFloat64Changed(double newValue); + + void setPropString(String propString); + String getPropString(); + void firePropStringChanged(String newValue); + + // methods + void funcNoReturnValue(boolean paramBool); + CompletableFuture funcNoReturnValueAsync(boolean paramBool); + boolean funcBool(boolean paramBool); + CompletableFuture funcBoolAsync(boolean paramBool); + int funcInt(int paramInt); + CompletableFuture funcIntAsync(int paramInt); + int funcInt32(int paramInt32); + CompletableFuture funcInt32Async(int paramInt32); + long funcInt64(long paramInt64); + CompletableFuture funcInt64Async(long paramInt64); + float funcFloat(float paramFloat); + CompletableFuture funcFloatAsync(float paramFloat); + float funcFloat32(float paramFloat32); + CompletableFuture funcFloat32Async(float paramFloat32); + double funcFloat64(double paramFloat); + CompletableFuture funcFloat64Async(double paramFloat); + String funcString(String paramString); + CompletableFuture funcStringAsync(String paramString); + public void fireSigBool(boolean paramBool); + public void fireSigInt(int paramInt); + public void fireSigInt32(int paramInt32); + public void fireSigInt64(long paramInt64); + public void fireSigFloat(float paramFloat); + public void fireSigFloat32(float paramFloat32); + public void fireSigFloat64(double paramFloat64); + public void fireSigString(String paramString); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISimpleInterfaceEventListener listener); + void removeEventListener(ISimpleInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterfaceEventListener.java new file mode 100644 index 0000000..d40b2c0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterfaceEventListener.java @@ -0,0 +1,21 @@ +package tbSimple.tbSimple_api; + + public interface ISimpleInterfaceEventListener { + void onPropBoolChanged(boolean newValue); + void onPropIntChanged(int newValue); + void onPropInt32Changed(int newValue); + void onPropInt64Changed(long newValue); + void onPropFloatChanged(float newValue); + void onPropFloat32Changed(float newValue); + void onPropFloat64Changed(double newValue); + void onPropStringChanged(String newValue); + void onSigBool(boolean paramBool); + void onSigInt(int paramInt); + void onSigInt32(int paramInt32); + void onSigInt64(long paramInt64); + void onSigFloat(float paramFloat); + void onSigFloat32(float paramFloat32); + void onSigFloat64(double paramFloat64); + void onSigString(String paramString); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterface.java new file mode 100644 index 0000000..846690e --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterface.java @@ -0,0 +1,19 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface IVoidInterface { + // properties + // methods + void funcVoid(); + CompletableFuture funcVoidAsync(); + public void fireSigVoid(); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IVoidInterfaceEventListener listener); + void removeEventListener(IVoidInterfaceEventListener listener); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterfaceEventListener.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterfaceEventListener.java new file mode 100644 index 0000000..1159b8c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/IVoidInterfaceEventListener.java @@ -0,0 +1,6 @@ +package tbSimple.tbSimple_api; + + public interface IVoidInterfaceEventListener { + void onSigVoid(); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java new file mode 100644 index 0000000..e712661 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java @@ -0,0 +1,11 @@ +package tbSimple.tbSimple_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class TbSimpleTestHelper +{ + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/build.gradle b/goldenmaster/tbSimple/tbSimple_client_example/build.gradle new file mode 100644 index 0000000..8f02747 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbSimple.tbSimple_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbSimple.tbSimple_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbSimple_api') + implementation project(':tbSimple_android_messenger') + implementation project(':tbSimple_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3001f70 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java new file mode 100644 index 0000000..10c83d1 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java @@ -0,0 +1,211 @@ +package tbSimple.tbSimple_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbSimple.tbSimple_android_client.VoidInterfaceClient; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbSimpleTestClientApp extends Activity implements IVoidInterfaceEventListener +{ + + private static final String TAG = "TbSimpleTestClientApp"; + + private VoidInterfaceClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbSimple.tbSimpleserviceexample.TbSimpleTestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bFuncVoid = new Button(this); + bFuncVoid.setText("funcVoid"); + + bFuncVoid.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD funcVoid "); + CompletableFuture method_res + = mClient.funcVoidAsync().thenApply( + i -> { + outputTextVieMethodRes.setText("Got funcVoid result "+ i); + return i; + }); + }); + bFuncVoid.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFuncVoid); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new VoidInterfaceClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onSigVoid() + { + String text = "Signal sigVoid "; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/colors.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/strings.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..dba8ee5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbSimpleTestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/themes.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/additions.gradle b/goldenmaster/tbSimple/tbSimple_impl/additions.gradle new file mode 100644 index 0000000..8793152 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbSimple.tbSimple_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_impl/build.gradle b/goldenmaster/tbSimple/tbSimple_impl/build.gradle new file mode 100644 index 0000000..92a67e5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbSimple" +version = "1.0.0" + +android { + namespace 'tbSimple.tbSimple_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbSimple_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java new file mode 100644 index 0000000..3758894 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java @@ -0,0 +1,41 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class EmptyInterfaceService extends AbstractEmptyInterface { + + private final static String TAG = "EmptyInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public EmptyInterfaceService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java new file mode 100644 index 0000000..39337ea --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java @@ -0,0 +1,103 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NoOperationsInterfaceService extends AbstractNoOperationsInterface { + + private final static String TAG = "NoOperationsInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private boolean m_propBool = false; + private int m_propInt = 0; + + public NoOperationsInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called "); + if (m_propBool != propBool) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called "); + if (m_propInt != propInt) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + // methods + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(int newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onSigVoid() + { + Log.i(TAG, "onSigVoid, will pass notification to all listeners"); + fireSigVoid(); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java new file mode 100644 index 0000000..f88099d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java @@ -0,0 +1,77 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NoPropertiesInterfaceService extends AbstractNoPropertiesInterface { + + private final static String TAG = "NoPropertiesInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NoPropertiesInterfaceService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.w(TAG, "request method funcVoid called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return false; + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + public void onSigVoid() + { + Log.i(TAG, "onSigVoid, will pass notification to all listeners"); + fireSigVoid(); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java new file mode 100644 index 0000000..dc58d10 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java @@ -0,0 +1,119 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NoSignalsInterfaceService extends AbstractNoSignalsInterface { + + private final static String TAG = "NoSignalsInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private boolean m_propBool = false; + private int m_propInt = 0; + + public NoSignalsInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called "); + if (m_propBool != propBool) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called "); + if (m_propInt != propInt) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + // methods + + @Override + public void funcVoid() { + Log.w(TAG, "request method funcVoid called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return false; + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(int newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java new file mode 100644 index 0000000..5f8fa99 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java @@ -0,0 +1,419 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SimpleArrayInterfaceService extends AbstractSimpleArrayInterface { + + private final static String TAG = "SimpleArrayInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private boolean[] m_propBool = new boolean[]{}; + private int[] m_propInt = new int[]{}; + private int[] m_propInt32 = new int[]{}; + private long[] m_propInt64 = new long[]{}; + private float[] m_propFloat = new float[]{}; + private float[] m_propFloat32 = new float[]{}; + private double[] m_propFloat64 = new double[]{}; + private String[] m_propString = new String[]{}; + private String m_propReadOnlyString = new String(); + + public SimpleArrayInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean[] propBool) + { + Log.i(TAG, "request setPropBool called "); + if (! Arrays.equals(m_propBool, propBool)) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public boolean[] getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(int[] propInt) + { + Log.i(TAG, "request setPropInt called "); + if (! Arrays.equals(m_propInt, propInt)) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public int[] getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + @Override + public void setPropInt32(int[] propInt32) + { + Log.i(TAG, "request setPropInt32 called "); + if (! Arrays.equals(m_propInt32, propInt32)) + { + m_propInt32 = propInt32; + onPropInt32Changed(m_propInt32); + } + + } + + @Override + public int[] getPropInt32() + { + Log.i(TAG, "request getPropInt32 called,"); + return m_propInt32; + } + + + @Override + public void setPropInt64(long[] propInt64) + { + Log.i(TAG, "request setPropInt64 called "); + if (! Arrays.equals(m_propInt64, propInt64)) + { + m_propInt64 = propInt64; + onPropInt64Changed(m_propInt64); + } + + } + + @Override + public long[] getPropInt64() + { + Log.i(TAG, "request getPropInt64 called,"); + return m_propInt64; + } + + + @Override + public void setPropFloat(float[] propFloat) + { + Log.i(TAG, "request setPropFloat called "); + if (! Arrays.equals(m_propFloat, propFloat)) + { + m_propFloat = propFloat; + onPropFloatChanged(m_propFloat); + } + + } + + @Override + public float[] getPropFloat() + { + Log.i(TAG, "request getPropFloat called,"); + return m_propFloat; + } + + + @Override + public void setPropFloat32(float[] propFloat32) + { + Log.i(TAG, "request setPropFloat32 called "); + if (! Arrays.equals(m_propFloat32, propFloat32)) + { + m_propFloat32 = propFloat32; + onPropFloat32Changed(m_propFloat32); + } + + } + + @Override + public float[] getPropFloat32() + { + Log.i(TAG, "request getPropFloat32 called,"); + return m_propFloat32; + } + + + @Override + public void setPropFloat64(double[] propFloat64) + { + Log.i(TAG, "request setPropFloat64 called "); + if (! Arrays.equals(m_propFloat64, propFloat64)) + { + m_propFloat64 = propFloat64; + onPropFloat64Changed(m_propFloat64); + } + + } + + @Override + public double[] getPropFloat64() + { + Log.i(TAG, "request getPropFloat64 called,"); + return m_propFloat64; + } + + + @Override + public void setPropString(String[] propString) + { + Log.i(TAG, "request setPropString called "); + if (! Arrays.equals(m_propString, propString)) + { + m_propString = propString; + onPropStringChanged(m_propString); + } + + } + + @Override + public String[] getPropString() + { + Log.i(TAG, "request getPropString called,"); + return m_propString; + } + + + @Override + public void setPropReadOnlyString(String propReadOnlyString) + { + Log.i(TAG, "request setPropReadOnlyString called "); + if (m_propReadOnlyString != propReadOnlyString) + { + m_propReadOnlyString = propReadOnlyString; + onPropReadOnlyStringChanged(m_propReadOnlyString); + } + + } + + @Override + public String getPropReadOnlyString() + { + Log.i(TAG, "request getPropReadOnlyString called,"); + return m_propReadOnlyString; + } + + + // methods + + @Override + public boolean[] funcBool(boolean[] paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return new boolean[]{}; + } + + @Override + public CompletableFuture funcBoolAsync(boolean[] paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public int[] funcInt(int[] paramInt) { + Log.w(TAG, "request method funcInt called, returnig default"); + return new int[]{}; + } + + @Override + public CompletableFuture funcIntAsync(int[] paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public int[] funcInt32(int[] paramInt32) { + Log.w(TAG, "request method funcInt32 called, returnig default"); + return new int[]{}; + } + + @Override + public CompletableFuture funcInt32Async(int[] paramInt32) { + return CompletableFuture.supplyAsync( + () -> {return funcInt32(paramInt32); }, + executor); + } + + @Override + public long[] funcInt64(long[] paramInt64) { + Log.w(TAG, "request method funcInt64 called, returnig default"); + return new long[]{}; + } + + @Override + public CompletableFuture funcInt64Async(long[] paramInt64) { + return CompletableFuture.supplyAsync( + () -> {return funcInt64(paramInt64); }, + executor); + } + + @Override + public float[] funcFloat(float[] paramFloat) { + Log.w(TAG, "request method funcFloat called, returnig default"); + return new float[]{}; + } + + @Override + public CompletableFuture funcFloatAsync(float[] paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public float[] funcFloat32(float[] paramFloat32) { + Log.w(TAG, "request method funcFloat32 called, returnig default"); + return new float[]{}; + } + + @Override + public CompletableFuture funcFloat32Async(float[] paramFloat32) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat32(paramFloat32); }, + executor); + } + + @Override + public double[] funcFloat64(double[] paramFloat) { + Log.w(TAG, "request method funcFloat64 called, returnig default"); + return new double[]{}; + } + + @Override + public CompletableFuture funcFloat64Async(double[] paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat64(paramFloat); }, + executor); + } + + @Override + public String[] funcString(String[] paramString) { + Log.w(TAG, "request method funcString called, returnig default"); + return new String[]{}; + } + + @Override + public CompletableFuture funcStringAsync(String[] paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(boolean[] newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(int[] newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + private void onPropInt32Changed(int[] newValue) + { + Log.i(TAG, "onPropInt32Changed, will pass notification to all listeners"); + firePropInt32Changed(newValue); + } + private void onPropInt64Changed(long[] newValue) + { + Log.i(TAG, "onPropInt64Changed, will pass notification to all listeners"); + firePropInt64Changed(newValue); + } + private void onPropFloatChanged(float[] newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + private void onPropFloat32Changed(float[] newValue) + { + Log.i(TAG, "onPropFloat32Changed, will pass notification to all listeners"); + firePropFloat32Changed(newValue); + } + private void onPropFloat64Changed(double[] newValue) + { + Log.i(TAG, "onPropFloat64Changed, will pass notification to all listeners"); + firePropFloat64Changed(newValue); + } + private void onPropStringChanged(String[] newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + private void onPropReadOnlyStringChanged(String newValue) + { + Log.i(TAG, "onPropReadOnlyStringChanged, will pass notification to all listeners"); + firePropReadOnlyStringChanged(newValue); + } + public void onSigBool(boolean[] paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(int[] paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigInt32(int[] paramInt32) + { + Log.i(TAG, "onSigInt32, will pass notification to all listeners"); + fireSigInt32(paramInt32); + } + public void onSigInt64(long[] paramInt64) + { + Log.i(TAG, "onSigInt64, will pass notification to all listeners"); + fireSigInt64(paramInt64); + } + public void onSigFloat(float[] paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigFloat32(float[] paramFloa32) + { + Log.i(TAG, "onSigFloat32, will pass notification to all listeners"); + fireSigFloat32(paramFloa32); + } + public void onSigFloat64(double[] paramFloat64) + { + Log.i(TAG, "onSigFloat64, will pass notification to all listeners"); + fireSigFloat64(paramFloat64); + } + public void onSigString(String[] paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java new file mode 100644 index 0000000..01e7ad0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java @@ -0,0 +1,406 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SimpleInterfaceService extends AbstractSimpleInterface { + + private final static String TAG = "SimpleInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private boolean m_propBool = false; + private int m_propInt = 0; + private int m_propInt32 = 0; + private long m_propInt64 = 0L; + private float m_propFloat = 0.0f; + private float m_propFloat32 = 0.0f; + private double m_propFloat64 = 0.0; + private String m_propString = new String(); + + public SimpleInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called "); + if (m_propBool != propBool) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called "); + if (m_propInt != propInt) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + @Override + public void setPropInt32(int propInt32) + { + Log.i(TAG, "request setPropInt32 called "); + if (m_propInt32 != propInt32) + { + m_propInt32 = propInt32; + onPropInt32Changed(m_propInt32); + } + + } + + @Override + public int getPropInt32() + { + Log.i(TAG, "request getPropInt32 called,"); + return m_propInt32; + } + + + @Override + public void setPropInt64(long propInt64) + { + Log.i(TAG, "request setPropInt64 called "); + if (m_propInt64 != propInt64) + { + m_propInt64 = propInt64; + onPropInt64Changed(m_propInt64); + } + + } + + @Override + public long getPropInt64() + { + Log.i(TAG, "request getPropInt64 called,"); + return m_propInt64; + } + + + @Override + public void setPropFloat(float propFloat) + { + Log.i(TAG, "request setPropFloat called "); + if (m_propFloat != propFloat) + { + m_propFloat = propFloat; + onPropFloatChanged(m_propFloat); + } + + } + + @Override + public float getPropFloat() + { + Log.i(TAG, "request getPropFloat called,"); + return m_propFloat; + } + + + @Override + public void setPropFloat32(float propFloat32) + { + Log.i(TAG, "request setPropFloat32 called "); + if (m_propFloat32 != propFloat32) + { + m_propFloat32 = propFloat32; + onPropFloat32Changed(m_propFloat32); + } + + } + + @Override + public float getPropFloat32() + { + Log.i(TAG, "request getPropFloat32 called,"); + return m_propFloat32; + } + + + @Override + public void setPropFloat64(double propFloat64) + { + Log.i(TAG, "request setPropFloat64 called "); + if (m_propFloat64 != propFloat64) + { + m_propFloat64 = propFloat64; + onPropFloat64Changed(m_propFloat64); + } + + } + + @Override + public double getPropFloat64() + { + Log.i(TAG, "request getPropFloat64 called,"); + return m_propFloat64; + } + + + @Override + public void setPropString(String propString) + { + Log.i(TAG, "request setPropString called "); + if (m_propString != propString) + { + m_propString = propString; + onPropStringChanged(m_propString); + } + + } + + @Override + public String getPropString() + { + Log.i(TAG, "request getPropString called,"); + return m_propString; + } + + + // methods + + @Override + public void funcNoReturnValue(boolean paramBool) { + Log.w(TAG, "request method funcNoReturnValue called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { + return CompletableFuture.runAsync( + () -> { funcNoReturnValue(paramBool); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return false; + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public int funcInt(int paramInt) { + Log.w(TAG, "request method funcInt called, returnig default"); + return 0; + } + + @Override + public CompletableFuture funcIntAsync(int paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public int funcInt32(int paramInt32) { + Log.w(TAG, "request method funcInt32 called, returnig default"); + return 0; + } + + @Override + public CompletableFuture funcInt32Async(int paramInt32) { + return CompletableFuture.supplyAsync( + () -> {return funcInt32(paramInt32); }, + executor); + } + + @Override + public long funcInt64(long paramInt64) { + Log.w(TAG, "request method funcInt64 called, returnig default"); + return 0L; + } + + @Override + public CompletableFuture funcInt64Async(long paramInt64) { + return CompletableFuture.supplyAsync( + () -> {return funcInt64(paramInt64); }, + executor); + } + + @Override + public float funcFloat(float paramFloat) { + Log.w(TAG, "request method funcFloat called, returnig default"); + return 0.0f; + } + + @Override + public CompletableFuture funcFloatAsync(float paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public float funcFloat32(float paramFloat32) { + Log.w(TAG, "request method funcFloat32 called, returnig default"); + return 0.0f; + } + + @Override + public CompletableFuture funcFloat32Async(float paramFloat32) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat32(paramFloat32); }, + executor); + } + + @Override + public double funcFloat64(double paramFloat) { + Log.w(TAG, "request method funcFloat64 called, returnig default"); + return 0.0; + } + + @Override + public CompletableFuture funcFloat64Async(double paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat64(paramFloat); }, + executor); + } + + @Override + public String funcString(String paramString) { + Log.w(TAG, "request method funcString called, returnig default"); + return new String(); + } + + @Override + public CompletableFuture funcStringAsync(String paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(int newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + private void onPropInt32Changed(int newValue) + { + Log.i(TAG, "onPropInt32Changed, will pass notification to all listeners"); + firePropInt32Changed(newValue); + } + private void onPropInt64Changed(long newValue) + { + Log.i(TAG, "onPropInt64Changed, will pass notification to all listeners"); + firePropInt64Changed(newValue); + } + private void onPropFloatChanged(float newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + private void onPropFloat32Changed(float newValue) + { + Log.i(TAG, "onPropFloat32Changed, will pass notification to all listeners"); + firePropFloat32Changed(newValue); + } + private void onPropFloat64Changed(double newValue) + { + Log.i(TAG, "onPropFloat64Changed, will pass notification to all listeners"); + firePropFloat64Changed(newValue); + } + private void onPropStringChanged(String newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(int paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigInt32(int paramInt32) + { + Log.i(TAG, "onSigInt32, will pass notification to all listeners"); + fireSigInt32(paramInt32); + } + public void onSigInt64(long paramInt64) + { + Log.i(TAG, "onSigInt64, will pass notification to all listeners"); + fireSigInt64(paramInt64); + } + public void onSigFloat(float paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigFloat32(float paramFloat32) + { + Log.i(TAG, "onSigFloat32, will pass notification to all listeners"); + fireSigFloat32(paramFloat32); + } + public void onSigFloat64(double paramFloat64) + { + Log.i(TAG, "onSigFloat64, will pass notification to all listeners"); + fireSigFloat64(paramFloat64); + } + public void onSigString(String paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java new file mode 100644 index 0000000..6b259fb --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java @@ -0,0 +1,59 @@ +package tbSimple.tbSimple_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class VoidInterfaceService extends AbstractVoidInterface { + + private final static String TAG = "VoidInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public VoidInterfaceService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.w(TAG, "request method funcVoid called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + public void onSigVoid() + { + Log.i(TAG, "onSigVoid, will pass notification to all listeners"); + fireSigVoid(); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java new file mode 100644 index 0000000..fcc3fec --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java @@ -0,0 +1,68 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; + +import tbSimple.tbSimple_android_client.EmptyInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class EmptyInterfaceJniClient extends AbstractEmptyInterface implements IEmptyInterfaceEventListener +{ + + private static final String TAG = "EmptyInterfaceJniClient"; + + private EmptyInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.EmptyInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new EmptyInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java new file mode 100644 index 0000000..40e43fc --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java @@ -0,0 +1,122 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; + +import tbSimple.tbSimple_android_client.NoOperationsInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NoOperationsInterfaceJniClient extends AbstractNoOperationsInterface implements INoOperationsInterfaceEventListener +{ + + private static final String TAG = "NoOperationsInterfaceJniClient"; + + private NoOperationsInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.NoOperationsInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public boolean getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public int getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NoOperationsInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onSigVoid() + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + nativeOnSigVoid(); + } + @Override + public void onSigBool(boolean paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + private native void nativeOnPropBoolChanged(boolean propBool); + private native void nativeOnPropIntChanged(int propInt); + private native void nativeOnSigVoid(); + private native void nativeOnSigBool(boolean paramBool); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java new file mode 100644 index 0000000..9197d3d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java @@ -0,0 +1,120 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; + +import tbSimple.tbSimple_android_client.NoPropertiesInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NoPropertiesInterfaceJniClient extends AbstractNoPropertiesInterface implements INoPropertiesInterfaceEventListener +{ + + private static final String TAG = "NoPropertiesInterfaceJniClient"; + + private NoPropertiesInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.NoPropertiesInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + public void funcVoid() + { + Log.v(TAG, "Blocking callfuncVoid - should not be used "); + mMessengerClient.funcVoid(); + } + + public void funcVoidAsync(String callId){ + Log.v(TAG, "non blocking call funcVoid "); + mMessengerClient.funcVoidAsync().thenAccept(i -> { + nativeOnFuncVoidResult(callId);}); + } + + //Should not be called directly, use funcVoidAsync(String callId, ) + public CompletableFuture funcVoidAsync() + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcVoidAsync(); + } + public boolean funcBool(boolean paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, boolean paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, boolean paramBool) + public CompletableFuture funcBoolAsync(boolean paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NoPropertiesInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onSigVoid() + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + nativeOnSigVoid(); + } + @Override + public void onSigBool(boolean paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + private native void nativeOnSigVoid(); + private native void nativeOnSigBool(boolean paramBool); + private native void nativeOnFuncVoidResult(String callId); + private native void nativeOnFuncBoolResult(boolean result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java new file mode 100644 index 0000000..9c8de6a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java @@ -0,0 +1,146 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; + +import tbSimple.tbSimple_android_client.NoSignalsInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NoSignalsInterfaceJniClient extends AbstractNoSignalsInterface implements INoSignalsInterfaceEventListener +{ + + private static final String TAG = "NoSignalsInterfaceJniClient"; + + private NoSignalsInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.NoSignalsInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public boolean getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public int getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + public void funcVoid() + { + Log.v(TAG, "Blocking callfuncVoid - should not be used "); + mMessengerClient.funcVoid(); + } + + public void funcVoidAsync(String callId){ + Log.v(TAG, "non blocking call funcVoid "); + mMessengerClient.funcVoidAsync().thenAccept(i -> { + nativeOnFuncVoidResult(callId);}); + } + + //Should not be called directly, use funcVoidAsync(String callId, ) + public CompletableFuture funcVoidAsync() + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcVoidAsync(); + } + public boolean funcBool(boolean paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, boolean paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, boolean paramBool) + public CompletableFuture funcBoolAsync(boolean paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NoSignalsInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + private native void nativeOnPropBoolChanged(boolean propBool); + private native void nativeOnPropIntChanged(int propInt); + private native void nativeOnFuncVoidResult(String callId); + private native void nativeOnFuncBoolResult(boolean result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java new file mode 100644 index 0000000..d6018b8 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java @@ -0,0 +1,456 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; + +import tbSimple.tbSimple_android_client.SimpleArrayInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SimpleArrayInterfaceJniClient extends AbstractSimpleArrayInterface implements ISimpleArrayInterfaceEventListener +{ + + private static final String TAG = "SimpleArrayInterfaceJniClient"; + + private SimpleArrayInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.SimpleArrayInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(boolean[] propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public boolean[] getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(int[] propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public int[] getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + @Override + public void setPropInt32(int[] propInt32) + { + Log.i(TAG, "got request from ue, setPropInt32" + (propInt32)); + mMessengerClient.setPropInt32(propInt32); + } + @Override + public int[] getPropInt32() + { + Log.i(TAG, "got request from ue, getPropInt32"); + return mMessengerClient.getPropInt32(); + } + + @Override + public void setPropInt64(long[] propInt64) + { + Log.i(TAG, "got request from ue, setPropInt64" + (propInt64)); + mMessengerClient.setPropInt64(propInt64); + } + @Override + public long[] getPropInt64() + { + Log.i(TAG, "got request from ue, getPropInt64"); + return mMessengerClient.getPropInt64(); + } + + @Override + public void setPropFloat(float[] propFloat) + { + Log.i(TAG, "got request from ue, setPropFloat" + (propFloat)); + mMessengerClient.setPropFloat(propFloat); + } + @Override + public float[] getPropFloat() + { + Log.i(TAG, "got request from ue, getPropFloat"); + return mMessengerClient.getPropFloat(); + } + + @Override + public void setPropFloat32(float[] propFloat32) + { + Log.i(TAG, "got request from ue, setPropFloat32" + (propFloat32)); + mMessengerClient.setPropFloat32(propFloat32); + } + @Override + public float[] getPropFloat32() + { + Log.i(TAG, "got request from ue, getPropFloat32"); + return mMessengerClient.getPropFloat32(); + } + + @Override + public void setPropFloat64(double[] propFloat64) + { + Log.i(TAG, "got request from ue, setPropFloat64" + (propFloat64)); + mMessengerClient.setPropFloat64(propFloat64); + } + @Override + public double[] getPropFloat64() + { + Log.i(TAG, "got request from ue, getPropFloat64"); + return mMessengerClient.getPropFloat64(); + } + + @Override + public void setPropString(String[] propString) + { + Log.i(TAG, "got request from ue, setPropString" + (propString)); + mMessengerClient.setPropString(propString); + } + @Override + public String[] getPropString() + { + Log.i(TAG, "got request from ue, getPropString"); + return mMessengerClient.getPropString(); + } + + @Override + public void setPropReadOnlyString(String propReadOnlyString) + { + Log.i(TAG, "got request from ue, setPropReadOnlyString" + (propReadOnlyString)); + mMessengerClient.setPropReadOnlyString(propReadOnlyString); + } + @Override + public String getPropReadOnlyString() + { + Log.i(TAG, "got request from ue, getPropReadOnlyString"); + return mMessengerClient.getPropReadOnlyString(); + } + + public boolean[] funcBool(boolean[] paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, boolean[] paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, boolean[] paramBool) + public CompletableFuture funcBoolAsync(boolean[] paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + public int[] funcInt(int[] paramInt) + { + Log.v(TAG, "Blocking callfuncInt - should not be used "); + return mMessengerClient.funcInt(paramInt); + } + + public void funcIntAsync(String callId, int[] paramInt){ + Log.v(TAG, "non blocking call funcInt "); + mMessengerClient.funcIntAsync(paramInt).thenAccept(i -> { + nativeOnFuncIntResult(i, callId);}); + } + + //Should not be called directly, use funcIntAsync(String callId, int[] paramInt) + public CompletableFuture funcIntAsync(int[] paramInt) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcIntAsync(paramInt); + } + public int[] funcInt32(int[] paramInt32) + { + Log.v(TAG, "Blocking callfuncInt32 - should not be used "); + return mMessengerClient.funcInt32(paramInt32); + } + + public void funcInt32Async(String callId, int[] paramInt32){ + Log.v(TAG, "non blocking call funcInt32 "); + mMessengerClient.funcInt32Async(paramInt32).thenAccept(i -> { + nativeOnFuncInt32Result(i, callId);}); + } + + //Should not be called directly, use funcInt32Async(String callId, int[] paramInt32) + public CompletableFuture funcInt32Async(int[] paramInt32) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcInt32Async(paramInt32); + } + public long[] funcInt64(long[] paramInt64) + { + Log.v(TAG, "Blocking callfuncInt64 - should not be used "); + return mMessengerClient.funcInt64(paramInt64); + } + + public void funcInt64Async(String callId, long[] paramInt64){ + Log.v(TAG, "non blocking call funcInt64 "); + mMessengerClient.funcInt64Async(paramInt64).thenAccept(i -> { + nativeOnFuncInt64Result(i, callId);}); + } + + //Should not be called directly, use funcInt64Async(String callId, long[] paramInt64) + public CompletableFuture funcInt64Async(long[] paramInt64) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcInt64Async(paramInt64); + } + public float[] funcFloat(float[] paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat - should not be used "); + return mMessengerClient.funcFloat(paramFloat); + } + + public void funcFloatAsync(String callId, float[] paramFloat){ + Log.v(TAG, "non blocking call funcFloat "); + mMessengerClient.funcFloatAsync(paramFloat).thenAccept(i -> { + nativeOnFuncFloatResult(i, callId);}); + } + + //Should not be called directly, use funcFloatAsync(String callId, float[] paramFloat) + public CompletableFuture funcFloatAsync(float[] paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloatAsync(paramFloat); + } + public float[] funcFloat32(float[] paramFloat32) + { + Log.v(TAG, "Blocking callfuncFloat32 - should not be used "); + return mMessengerClient.funcFloat32(paramFloat32); + } + + public void funcFloat32Async(String callId, float[] paramFloat32){ + Log.v(TAG, "non blocking call funcFloat32 "); + mMessengerClient.funcFloat32Async(paramFloat32).thenAccept(i -> { + nativeOnFuncFloat32Result(i, callId);}); + } + + //Should not be called directly, use funcFloat32Async(String callId, float[] paramFloat32) + public CompletableFuture funcFloat32Async(float[] paramFloat32) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloat32Async(paramFloat32); + } + public double[] funcFloat64(double[] paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat64 - should not be used "); + return mMessengerClient.funcFloat64(paramFloat); + } + + public void funcFloat64Async(String callId, double[] paramFloat){ + Log.v(TAG, "non blocking call funcFloat64 "); + mMessengerClient.funcFloat64Async(paramFloat).thenAccept(i -> { + nativeOnFuncFloat64Result(i, callId);}); + } + + //Should not be called directly, use funcFloat64Async(String callId, double[] paramFloat) + public CompletableFuture funcFloat64Async(double[] paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloat64Async(paramFloat); + } + public String[] funcString(String[] paramString) + { + Log.v(TAG, "Blocking callfuncString - should not be used "); + return mMessengerClient.funcString(paramString); + } + + public void funcStringAsync(String callId, String[] paramString){ + Log.v(TAG, "non blocking call funcString "); + mMessengerClient.funcStringAsync(paramString).thenAccept(i -> { + nativeOnFuncStringResult(i, callId);}); + } + + //Should not be called directly, use funcStringAsync(String callId, String[] paramString) + public CompletableFuture funcStringAsync(String[] paramString) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcStringAsync(paramString); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SimpleArrayInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropInt32Changed(int[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt32Changed(newValue); + } + @Override + public void onPropInt64Changed(long[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt64Changed(newValue); + } + @Override + public void onPropFloatChanged(float[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropFloat32Changed(float[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat32Changed(newValue); + } + @Override + public void onPropFloat64Changed(double[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat64Changed(newValue); + } + @Override + public void onPropStringChanged(String[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onPropReadOnlyStringChanged(String newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropReadOnlyStringChanged(newValue); + } + @Override + public void onSigBool(boolean[] paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(int[] paramInt) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigInt32(int[] paramInt32) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); + nativeOnSigInt32(paramInt32); + } + @Override + public void onSigInt64(long[] paramInt64) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); + nativeOnSigInt64(paramInt64); + } + @Override + public void onSigFloat(float[] paramFloat) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigFloat32(float[] paramFloa32) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloa32); + nativeOnSigFloat32(paramFloa32); + } + @Override + public void onSigFloat64(double[] paramFloat64) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); + nativeOnSigFloat64(paramFloat64); + } + @Override + public void onSigString(String[] paramString) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + nativeOnSigString(paramString); + } + private native void nativeOnPropBoolChanged(boolean[] propBool); + private native void nativeOnPropIntChanged(int[] propInt); + private native void nativeOnPropInt32Changed(int[] propInt32); + private native void nativeOnPropInt64Changed(long[] propInt64); + private native void nativeOnPropFloatChanged(float[] propFloat); + private native void nativeOnPropFloat32Changed(float[] propFloat32); + private native void nativeOnPropFloat64Changed(double[] propFloat64); + private native void nativeOnPropStringChanged(String[] propString); + private native void nativeOnPropReadOnlyStringChanged(String propReadOnlyString); + private native void nativeOnSigBool(boolean[] paramBool); + private native void nativeOnSigInt(int[] paramInt); + private native void nativeOnSigInt32(int[] paramInt32); + private native void nativeOnSigInt64(long[] paramInt64); + private native void nativeOnSigFloat(float[] paramFloat); + private native void nativeOnSigFloat32(float[] paramFloa32); + private native void nativeOnSigFloat64(double[] paramFloat64); + private native void nativeOnSigString(String[] paramString); + private native void nativeOnFuncBoolResult(boolean[] result, String callId); + private native void nativeOnFuncIntResult(int[] result, String callId); + private native void nativeOnFuncInt32Result(int[] result, String callId); + private native void nativeOnFuncInt64Result(long[] result, String callId); + private native void nativeOnFuncFloatResult(float[] result, String callId); + private native void nativeOnFuncFloat32Result(float[] result, String callId); + private native void nativeOnFuncFloat64Result(double[] result, String callId); + private native void nativeOnFuncStringResult(String[] result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java new file mode 100644 index 0000000..0dee43b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java @@ -0,0 +1,455 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; + +import tbSimple.tbSimple_android_client.SimpleInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SimpleInterfaceJniClient extends AbstractSimpleInterface implements ISimpleInterfaceEventListener +{ + + private static final String TAG = "SimpleInterfaceJniClient"; + + private SimpleInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.SimpleInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public boolean getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public int getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + @Override + public void setPropInt32(int propInt32) + { + Log.i(TAG, "got request from ue, setPropInt32" + (propInt32)); + mMessengerClient.setPropInt32(propInt32); + } + @Override + public int getPropInt32() + { + Log.i(TAG, "got request from ue, getPropInt32"); + return mMessengerClient.getPropInt32(); + } + + @Override + public void setPropInt64(long propInt64) + { + Log.i(TAG, "got request from ue, setPropInt64" + (propInt64)); + mMessengerClient.setPropInt64(propInt64); + } + @Override + public long getPropInt64() + { + Log.i(TAG, "got request from ue, getPropInt64"); + return mMessengerClient.getPropInt64(); + } + + @Override + public void setPropFloat(float propFloat) + { + Log.i(TAG, "got request from ue, setPropFloat" + (propFloat)); + mMessengerClient.setPropFloat(propFloat); + } + @Override + public float getPropFloat() + { + Log.i(TAG, "got request from ue, getPropFloat"); + return mMessengerClient.getPropFloat(); + } + + @Override + public void setPropFloat32(float propFloat32) + { + Log.i(TAG, "got request from ue, setPropFloat32" + (propFloat32)); + mMessengerClient.setPropFloat32(propFloat32); + } + @Override + public float getPropFloat32() + { + Log.i(TAG, "got request from ue, getPropFloat32"); + return mMessengerClient.getPropFloat32(); + } + + @Override + public void setPropFloat64(double propFloat64) + { + Log.i(TAG, "got request from ue, setPropFloat64" + (propFloat64)); + mMessengerClient.setPropFloat64(propFloat64); + } + @Override + public double getPropFloat64() + { + Log.i(TAG, "got request from ue, getPropFloat64"); + return mMessengerClient.getPropFloat64(); + } + + @Override + public void setPropString(String propString) + { + Log.i(TAG, "got request from ue, setPropString" + (propString)); + mMessengerClient.setPropString(propString); + } + @Override + public String getPropString() + { + Log.i(TAG, "got request from ue, getPropString"); + return mMessengerClient.getPropString(); + } + + public void funcNoReturnValue(boolean paramBool) + { + Log.v(TAG, "Blocking callfuncNoReturnValue - should not be used "); + mMessengerClient.funcNoReturnValue(paramBool); + } + + public void funcNoReturnValueAsync(String callId, boolean paramBool){ + Log.v(TAG, "non blocking call funcNoReturnValue "); + mMessengerClient.funcNoReturnValueAsync(paramBool).thenAccept(i -> { + nativeOnFuncNoReturnValueResult(callId);}); + } + + //Should not be called directly, use funcNoReturnValueAsync(String callId, boolean paramBool) + public CompletableFuture funcNoReturnValueAsync(boolean paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcNoReturnValueAsync(paramBool); + } + public boolean funcBool(boolean paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, boolean paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, boolean paramBool) + public CompletableFuture funcBoolAsync(boolean paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + public int funcInt(int paramInt) + { + Log.v(TAG, "Blocking callfuncInt - should not be used "); + return mMessengerClient.funcInt(paramInt); + } + + public void funcIntAsync(String callId, int paramInt){ + Log.v(TAG, "non blocking call funcInt "); + mMessengerClient.funcIntAsync(paramInt).thenAccept(i -> { + nativeOnFuncIntResult(i, callId);}); + } + + //Should not be called directly, use funcIntAsync(String callId, int paramInt) + public CompletableFuture funcIntAsync(int paramInt) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcIntAsync(paramInt); + } + public int funcInt32(int paramInt32) + { + Log.v(TAG, "Blocking callfuncInt32 - should not be used "); + return mMessengerClient.funcInt32(paramInt32); + } + + public void funcInt32Async(String callId, int paramInt32){ + Log.v(TAG, "non blocking call funcInt32 "); + mMessengerClient.funcInt32Async(paramInt32).thenAccept(i -> { + nativeOnFuncInt32Result(i, callId);}); + } + + //Should not be called directly, use funcInt32Async(String callId, int paramInt32) + public CompletableFuture funcInt32Async(int paramInt32) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcInt32Async(paramInt32); + } + public long funcInt64(long paramInt64) + { + Log.v(TAG, "Blocking callfuncInt64 - should not be used "); + return mMessengerClient.funcInt64(paramInt64); + } + + public void funcInt64Async(String callId, long paramInt64){ + Log.v(TAG, "non blocking call funcInt64 "); + mMessengerClient.funcInt64Async(paramInt64).thenAccept(i -> { + nativeOnFuncInt64Result(i, callId);}); + } + + //Should not be called directly, use funcInt64Async(String callId, long paramInt64) + public CompletableFuture funcInt64Async(long paramInt64) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcInt64Async(paramInt64); + } + public float funcFloat(float paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat - should not be used "); + return mMessengerClient.funcFloat(paramFloat); + } + + public void funcFloatAsync(String callId, float paramFloat){ + Log.v(TAG, "non blocking call funcFloat "); + mMessengerClient.funcFloatAsync(paramFloat).thenAccept(i -> { + nativeOnFuncFloatResult(i, callId);}); + } + + //Should not be called directly, use funcFloatAsync(String callId, float paramFloat) + public CompletableFuture funcFloatAsync(float paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloatAsync(paramFloat); + } + public float funcFloat32(float paramFloat32) + { + Log.v(TAG, "Blocking callfuncFloat32 - should not be used "); + return mMessengerClient.funcFloat32(paramFloat32); + } + + public void funcFloat32Async(String callId, float paramFloat32){ + Log.v(TAG, "non blocking call funcFloat32 "); + mMessengerClient.funcFloat32Async(paramFloat32).thenAccept(i -> { + nativeOnFuncFloat32Result(i, callId);}); + } + + //Should not be called directly, use funcFloat32Async(String callId, float paramFloat32) + public CompletableFuture funcFloat32Async(float paramFloat32) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloat32Async(paramFloat32); + } + public double funcFloat64(double paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat64 - should not be used "); + return mMessengerClient.funcFloat64(paramFloat); + } + + public void funcFloat64Async(String callId, double paramFloat){ + Log.v(TAG, "non blocking call funcFloat64 "); + mMessengerClient.funcFloat64Async(paramFloat).thenAccept(i -> { + nativeOnFuncFloat64Result(i, callId);}); + } + + //Should not be called directly, use funcFloat64Async(String callId, double paramFloat) + public CompletableFuture funcFloat64Async(double paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloat64Async(paramFloat); + } + public String funcString(String paramString) + { + Log.v(TAG, "Blocking callfuncString - should not be used "); + return mMessengerClient.funcString(paramString); + } + + public void funcStringAsync(String callId, String paramString){ + Log.v(TAG, "non blocking call funcString "); + mMessengerClient.funcStringAsync(paramString).thenAccept(i -> { + nativeOnFuncStringResult(i, callId);}); + } + + //Should not be called directly, use funcStringAsync(String callId, String paramString) + public CompletableFuture funcStringAsync(String paramString) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcStringAsync(paramString); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SimpleInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropInt32Changed(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt32Changed(newValue); + } + @Override + public void onPropInt64Changed(long newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt64Changed(newValue); + } + @Override + public void onPropFloatChanged(float newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropFloat32Changed(float newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat32Changed(newValue); + } + @Override + public void onPropFloat64Changed(double newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat64Changed(newValue); + } + @Override + public void onPropStringChanged(String newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onSigBool(boolean paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(int paramInt) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigInt32(int paramInt32) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); + nativeOnSigInt32(paramInt32); + } + @Override + public void onSigInt64(long paramInt64) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); + nativeOnSigInt64(paramInt64); + } + @Override + public void onSigFloat(float paramFloat) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigFloat32(float paramFloat32) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloat32); + nativeOnSigFloat32(paramFloat32); + } + @Override + public void onSigFloat64(double paramFloat64) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); + nativeOnSigFloat64(paramFloat64); + } + @Override + public void onSigString(String paramString) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + nativeOnSigString(paramString); + } + private native void nativeOnPropBoolChanged(boolean propBool); + private native void nativeOnPropIntChanged(int propInt); + private native void nativeOnPropInt32Changed(int propInt32); + private native void nativeOnPropInt64Changed(long propInt64); + private native void nativeOnPropFloatChanged(float propFloat); + private native void nativeOnPropFloat32Changed(float propFloat32); + private native void nativeOnPropFloat64Changed(double propFloat64); + private native void nativeOnPropStringChanged(String propString); + private native void nativeOnSigBool(boolean paramBool); + private native void nativeOnSigInt(int paramInt); + private native void nativeOnSigInt32(int paramInt32); + private native void nativeOnSigInt64(long paramInt64); + private native void nativeOnSigFloat(float paramFloat); + private native void nativeOnSigFloat32(float paramFloat32); + private native void nativeOnSigFloat64(double paramFloat64); + private native void nativeOnSigString(String paramString); + private native void nativeOnFuncNoReturnValueResult(String callId); + private native void nativeOnFuncBoolResult(boolean result, String callId); + private native void nativeOnFuncIntResult(int result, String callId); + private native void nativeOnFuncInt32Result(int result, String callId); + private native void nativeOnFuncInt64Result(long result, String callId); + private native void nativeOnFuncFloatResult(float result, String callId); + private native void nativeOnFuncFloat32Result(float result, String callId); + private native void nativeOnFuncFloat64Result(double result, String callId); + private native void nativeOnFuncStringResult(String result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java new file mode 100644 index 0000000..5d842af --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java @@ -0,0 +1,94 @@ +package tbSimple.tbSimplejniclient; + +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; + +import tbSimple.tbSimple_android_client.VoidInterfaceClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class VoidInterfaceJniClient extends AbstractVoidInterface implements IVoidInterfaceEventListener +{ + + private static final String TAG = "VoidInterfaceJniClient"; + + private VoidInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "tbSimple.tbSimplejniservice.VoidInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + public void funcVoid() + { + Log.v(TAG, "Blocking callfuncVoid - should not be used "); + mMessengerClient.funcVoid(); + } + + public void funcVoidAsync(String callId){ + Log.v(TAG, "non blocking call funcVoid "); + mMessengerClient.funcVoidAsync().thenAccept(i -> { + nativeOnFuncVoidResult(callId);}); + } + + //Should not be called directly, use funcVoidAsync(String callId, ) + public CompletableFuture funcVoidAsync() + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcVoidAsync(); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new VoidInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onSigVoid() + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + nativeOnSigVoid(); + } + private native void nativeOnSigVoid(); + private native void nativeOnFuncVoidResult(String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java new file mode 100644 index 0000000..d46b8ad --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java @@ -0,0 +1,49 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class EmptyInterfaceJniService extends AbstractEmptyInterface { + + + private final static String TAG = "EmptyInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public EmptyInterfaceJniService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + // methods + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java new file mode 100644 index 0000000..12774de --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.IEmptyInterfaceServiceFactory; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_api.AbstractEmptyInterface; +import tbSimple.tbSimplejniservice.EmptyInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton EmptyInterfaceJniServiceFactory thread for the system. This is a thread for + * EmptyInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class EmptyInterfaceJniServiceFactory extends HandlerThread implements IEmptyInterfaceServiceFactory +{ + private EmptyInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static EmptyInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: EmptyInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractEmptyInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new EmptyInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new EmptyInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final EmptyInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private EmptyInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static EmptyInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + EmptyInterfaceJniServiceFactory t = new EmptyInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java new file mode 100644 index 0000000..9259461 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_service.EmptyInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.EmptyInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class EmptyInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "EmptyInterfaceJniStarter"; + + + + public static IEmptyInterface start(Context context) { + stop(context); + androidService = new Intent(context, EmptyInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + EmptyInterfaceJniServiceFactory factory = EmptyInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for EmptyInterfaceJniServiceFactory"); + return EmptyInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java new file mode 100644 index 0000000..3e5590b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java @@ -0,0 +1,105 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NoOperationsInterfaceJniService extends AbstractNoOperationsInterface { + + + private final static String TAG = "NoOperationsInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NoOperationsInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + // methods + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(boolean propBool); + private native boolean nativeGetPropBool(); + + private native void nativeSetPropInt(int propInt); + private native int nativeGetPropInt(); + + // methods + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(int newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onSigVoid() + { + Log.i(TAG, "onSigVoid, will pass notification to all listeners"); + fireSigVoid(); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java new file mode 100644 index 0000000..5a89199 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.INoOperationsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_api.AbstractNoOperationsInterface; +import tbSimple.tbSimplejniservice.NoOperationsInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NoOperationsInterfaceJniServiceFactory thread for the system. This is a thread for + * NoOperationsInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NoOperationsInterfaceJniServiceFactory extends HandlerThread implements INoOperationsInterfaceServiceFactory +{ + private NoOperationsInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NoOperationsInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NoOperationsInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNoOperationsInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new NoOperationsInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NoOperationsInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NoOperationsInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private NoOperationsInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NoOperationsInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NoOperationsInterfaceJniServiceFactory t = new NoOperationsInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java new file mode 100644 index 0000000..b59a90a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_service.NoOperationsInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.NoOperationsInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NoOperationsInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NoOperationsInterfaceJniStarter"; + + + + public static INoOperationsInterface start(Context context) { + stop(context); + androidService = new Intent(context, NoOperationsInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NoOperationsInterfaceJniServiceFactory factory = NoOperationsInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NoOperationsInterfaceJniServiceFactory"); + return NoOperationsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java new file mode 100644 index 0000000..fa105a5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java @@ -0,0 +1,87 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NoPropertiesInterfaceJniService extends AbstractNoPropertiesInterface { + + + private final static String TAG = "NoPropertiesInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NoPropertiesInterfaceJniService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.w(TAG, "request method funcVoid called, will call native"); + nativeFuncVoid(); + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + // methods + private native void nativeFuncVoid(); + private native boolean nativeFuncBool(boolean paramBool); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onSigVoid() + { + Log.i(TAG, "onSigVoid, will pass notification to all listeners"); + fireSigVoid(); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java new file mode 100644 index 0000000..234d254 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.INoPropertiesInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; +import tbSimple.tbSimplejniservice.NoPropertiesInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NoPropertiesInterfaceJniServiceFactory thread for the system. This is a thread for + * NoPropertiesInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NoPropertiesInterfaceJniServiceFactory extends HandlerThread implements INoPropertiesInterfaceServiceFactory +{ + private NoPropertiesInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NoPropertiesInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NoPropertiesInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNoPropertiesInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new NoPropertiesInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NoPropertiesInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NoPropertiesInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private NoPropertiesInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NoPropertiesInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NoPropertiesInterfaceJniServiceFactory t = new NoPropertiesInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java new file mode 100644 index 0000000..60e249c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_service.NoPropertiesInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.NoPropertiesInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NoPropertiesInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NoPropertiesInterfaceJniStarter"; + + + + public static INoPropertiesInterface start(Context context) { + stop(context); + androidService = new Intent(context, NoPropertiesInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NoPropertiesInterfaceJniServiceFactory factory = NoPropertiesInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NoPropertiesInterfaceJniServiceFactory"); + return NoPropertiesInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java new file mode 100644 index 0000000..bea3154 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java @@ -0,0 +1,123 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NoSignalsInterfaceJniService extends AbstractNoSignalsInterface { + + + private final static String TAG = "NoSignalsInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NoSignalsInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + // methods + + @Override + public void funcVoid() { + Log.w(TAG, "request method funcVoid called, will call native"); + nativeFuncVoid(); + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(boolean propBool); + private native boolean nativeGetPropBool(); + + private native void nativeSetPropInt(int propInt); + private native int nativeGetPropInt(); + + // methods + private native void nativeFuncVoid(); + private native boolean nativeFuncBool(boolean paramBool); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(int newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java new file mode 100644 index 0000000..a2c9bc8 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.INoSignalsInterfaceServiceFactory; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_api.AbstractNoSignalsInterface; +import tbSimple.tbSimplejniservice.NoSignalsInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NoSignalsInterfaceJniServiceFactory thread for the system. This is a thread for + * NoSignalsInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NoSignalsInterfaceJniServiceFactory extends HandlerThread implements INoSignalsInterfaceServiceFactory +{ + private NoSignalsInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NoSignalsInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NoSignalsInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNoSignalsInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new NoSignalsInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NoSignalsInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NoSignalsInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private NoSignalsInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NoSignalsInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NoSignalsInterfaceJniServiceFactory t = new NoSignalsInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java new file mode 100644 index 0000000..ffbd1b1 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_service.NoSignalsInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.NoSignalsInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NoSignalsInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NoSignalsInterfaceJniStarter"; + + + + public static INoSignalsInterface start(Context context) { + stop(context); + androidService = new Intent(context, NoSignalsInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NoSignalsInterfaceJniServiceFactory factory = NoSignalsInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NoSignalsInterfaceJniServiceFactory"); + return NoSignalsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java new file mode 100644 index 0000000..e3ad193 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java @@ -0,0 +1,408 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SimpleArrayInterfaceJniService extends AbstractSimpleArrayInterface { + + + private final static String TAG = "SimpleArrayInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SimpleArrayInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean[] propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public boolean[] getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(int[] propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public int[] getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + @Override + public void setPropInt32(int[] propInt32) + { + Log.i(TAG, "request setPropInt32 called, will call native "); + nativeSetPropInt32(propInt32); + } + + @Override + public int[] getPropInt32() + { + Log.i(TAG, "request getPropInt32 called, will call native "); + return nativeGetPropInt32(); + } + + + @Override + public void setPropInt64(long[] propInt64) + { + Log.i(TAG, "request setPropInt64 called, will call native "); + nativeSetPropInt64(propInt64); + } + + @Override + public long[] getPropInt64() + { + Log.i(TAG, "request getPropInt64 called, will call native "); + return nativeGetPropInt64(); + } + + + @Override + public void setPropFloat(float[] propFloat) + { + Log.i(TAG, "request setPropFloat called, will call native "); + nativeSetPropFloat(propFloat); + } + + @Override + public float[] getPropFloat() + { + Log.i(TAG, "request getPropFloat called, will call native "); + return nativeGetPropFloat(); + } + + + @Override + public void setPropFloat32(float[] propFloat32) + { + Log.i(TAG, "request setPropFloat32 called, will call native "); + nativeSetPropFloat32(propFloat32); + } + + @Override + public float[] getPropFloat32() + { + Log.i(TAG, "request getPropFloat32 called, will call native "); + return nativeGetPropFloat32(); + } + + + @Override + public void setPropFloat64(double[] propFloat64) + { + Log.i(TAG, "request setPropFloat64 called, will call native "); + nativeSetPropFloat64(propFloat64); + } + + @Override + public double[] getPropFloat64() + { + Log.i(TAG, "request getPropFloat64 called, will call native "); + return nativeGetPropFloat64(); + } + + + @Override + public void setPropString(String[] propString) + { + Log.i(TAG, "request setPropString called, will call native "); + nativeSetPropString(propString); + } + + @Override + public String[] getPropString() + { + Log.i(TAG, "request getPropString called, will call native "); + return nativeGetPropString(); + } + + + @Override + public void setPropReadOnlyString(String propReadOnlyString) + { + Log.i(TAG, "request setPropReadOnlyString called, will call native "); + nativeSetPropReadOnlyString(propReadOnlyString); + } + + @Override + public String getPropReadOnlyString() + { + Log.i(TAG, "request getPropReadOnlyString called, will call native "); + return nativeGetPropReadOnlyString(); + } + + + // methods + + @Override + public boolean[] funcBool(boolean[] paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(boolean[] paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public int[] funcInt(int[] paramInt) { + Log.w(TAG, "request method funcInt called, will call native"); + return nativeFuncInt(paramInt); + } + + @Override + public CompletableFuture funcIntAsync(int[] paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public int[] funcInt32(int[] paramInt32) { + Log.w(TAG, "request method funcInt32 called, will call native"); + return nativeFuncInt32(paramInt32); + } + + @Override + public CompletableFuture funcInt32Async(int[] paramInt32) { + return CompletableFuture.supplyAsync( + () -> {return funcInt32(paramInt32); }, + executor); + } + + @Override + public long[] funcInt64(long[] paramInt64) { + Log.w(TAG, "request method funcInt64 called, will call native"); + return nativeFuncInt64(paramInt64); + } + + @Override + public CompletableFuture funcInt64Async(long[] paramInt64) { + return CompletableFuture.supplyAsync( + () -> {return funcInt64(paramInt64); }, + executor); + } + + @Override + public float[] funcFloat(float[] paramFloat) { + Log.w(TAG, "request method funcFloat called, will call native"); + return nativeFuncFloat(paramFloat); + } + + @Override + public CompletableFuture funcFloatAsync(float[] paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public float[] funcFloat32(float[] paramFloat32) { + Log.w(TAG, "request method funcFloat32 called, will call native"); + return nativeFuncFloat32(paramFloat32); + } + + @Override + public CompletableFuture funcFloat32Async(float[] paramFloat32) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat32(paramFloat32); }, + executor); + } + + @Override + public double[] funcFloat64(double[] paramFloat) { + Log.w(TAG, "request method funcFloat64 called, will call native"); + return nativeFuncFloat64(paramFloat); + } + + @Override + public CompletableFuture funcFloat64Async(double[] paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat64(paramFloat); }, + executor); + } + + @Override + public String[] funcString(String[] paramString) { + Log.w(TAG, "request method funcString called, will call native"); + return nativeFuncString(paramString); + } + + @Override + public CompletableFuture funcStringAsync(String[] paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(boolean[] propBool); + private native boolean[] nativeGetPropBool(); + + private native void nativeSetPropInt(int[] propInt); + private native int[] nativeGetPropInt(); + + private native void nativeSetPropInt32(int[] propInt32); + private native int[] nativeGetPropInt32(); + + private native void nativeSetPropInt64(long[] propInt64); + private native long[] nativeGetPropInt64(); + + private native void nativeSetPropFloat(float[] propFloat); + private native float[] nativeGetPropFloat(); + + private native void nativeSetPropFloat32(float[] propFloat32); + private native float[] nativeGetPropFloat32(); + + private native void nativeSetPropFloat64(double[] propFloat64); + private native double[] nativeGetPropFloat64(); + + private native void nativeSetPropString(String[] propString); + private native String[] nativeGetPropString(); + + private native void nativeSetPropReadOnlyString(String propReadOnlyString); + private native String nativeGetPropReadOnlyString(); + + // methods + private native boolean[] nativeFuncBool(boolean[] paramBool); + private native int[] nativeFuncInt(int[] paramInt); + private native int[] nativeFuncInt32(int[] paramInt32); + private native long[] nativeFuncInt64(long[] paramInt64); + private native float[] nativeFuncFloat(float[] paramFloat); + private native float[] nativeFuncFloat32(float[] paramFloat32); + private native double[] nativeFuncFloat64(double[] paramFloat); + private native String[] nativeFuncString(String[] paramString); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(boolean[] newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(int[] newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onPropInt32Changed(int[] newValue) + { + Log.i(TAG, "onPropInt32Changed, will pass notification to all listeners"); + firePropInt32Changed(newValue); + } + public void onPropInt64Changed(long[] newValue) + { + Log.i(TAG, "onPropInt64Changed, will pass notification to all listeners"); + firePropInt64Changed(newValue); + } + public void onPropFloatChanged(float[] newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + public void onPropFloat32Changed(float[] newValue) + { + Log.i(TAG, "onPropFloat32Changed, will pass notification to all listeners"); + firePropFloat32Changed(newValue); + } + public void onPropFloat64Changed(double[] newValue) + { + Log.i(TAG, "onPropFloat64Changed, will pass notification to all listeners"); + firePropFloat64Changed(newValue); + } + public void onPropStringChanged(String[] newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onPropReadOnlyStringChanged(String newValue) + { + Log.i(TAG, "onPropReadOnlyStringChanged, will pass notification to all listeners"); + firePropReadOnlyStringChanged(newValue); + } + public void onSigBool(boolean[] paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(int[] paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigInt32(int[] paramInt32) + { + Log.i(TAG, "onSigInt32, will pass notification to all listeners"); + fireSigInt32(paramInt32); + } + public void onSigInt64(long[] paramInt64) + { + Log.i(TAG, "onSigInt64, will pass notification to all listeners"); + fireSigInt64(paramInt64); + } + public void onSigFloat(float[] paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigFloat32(float[] paramFloa32) + { + Log.i(TAG, "onSigFloat32, will pass notification to all listeners"); + fireSigFloat32(paramFloa32); + } + public void onSigFloat64(double[] paramFloat64) + { + Log.i(TAG, "onSigFloat64, will pass notification to all listeners"); + fireSigFloat64(paramFloat64); + } + public void onSigString(String[] paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java new file mode 100644 index 0000000..306ef8e --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.ISimpleArrayInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; +import tbSimple.tbSimplejniservice.SimpleArrayInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SimpleArrayInterfaceJniServiceFactory thread for the system. This is a thread for + * SimpleArrayInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SimpleArrayInterfaceJniServiceFactory extends HandlerThread implements ISimpleArrayInterfaceServiceFactory +{ + private SimpleArrayInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SimpleArrayInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SimpleArrayInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSimpleArrayInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new SimpleArrayInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SimpleArrayInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SimpleArrayInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SimpleArrayInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SimpleArrayInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SimpleArrayInterfaceJniServiceFactory t = new SimpleArrayInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java new file mode 100644 index 0000000..ad24360 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_service.SimpleArrayInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.SimpleArrayInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SimpleArrayInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SimpleArrayInterfaceJniStarter"; + + + + public static ISimpleArrayInterface start(Context context) { + stop(context); + androidService = new Intent(context, SimpleArrayInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SimpleArrayInterfaceJniServiceFactory factory = SimpleArrayInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SimpleArrayInterfaceJniServiceFactory"); + return SimpleArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java new file mode 100644 index 0000000..252325a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java @@ -0,0 +1,399 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SimpleInterfaceJniService extends AbstractSimpleInterface { + + + private final static String TAG = "SimpleInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SimpleInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(boolean propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public boolean getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(int propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public int getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + @Override + public void setPropInt32(int propInt32) + { + Log.i(TAG, "request setPropInt32 called, will call native "); + nativeSetPropInt32(propInt32); + } + + @Override + public int getPropInt32() + { + Log.i(TAG, "request getPropInt32 called, will call native "); + return nativeGetPropInt32(); + } + + + @Override + public void setPropInt64(long propInt64) + { + Log.i(TAG, "request setPropInt64 called, will call native "); + nativeSetPropInt64(propInt64); + } + + @Override + public long getPropInt64() + { + Log.i(TAG, "request getPropInt64 called, will call native "); + return nativeGetPropInt64(); + } + + + @Override + public void setPropFloat(float propFloat) + { + Log.i(TAG, "request setPropFloat called, will call native "); + nativeSetPropFloat(propFloat); + } + + @Override + public float getPropFloat() + { + Log.i(TAG, "request getPropFloat called, will call native "); + return nativeGetPropFloat(); + } + + + @Override + public void setPropFloat32(float propFloat32) + { + Log.i(TAG, "request setPropFloat32 called, will call native "); + nativeSetPropFloat32(propFloat32); + } + + @Override + public float getPropFloat32() + { + Log.i(TAG, "request getPropFloat32 called, will call native "); + return nativeGetPropFloat32(); + } + + + @Override + public void setPropFloat64(double propFloat64) + { + Log.i(TAG, "request setPropFloat64 called, will call native "); + nativeSetPropFloat64(propFloat64); + } + + @Override + public double getPropFloat64() + { + Log.i(TAG, "request getPropFloat64 called, will call native "); + return nativeGetPropFloat64(); + } + + + @Override + public void setPropString(String propString) + { + Log.i(TAG, "request setPropString called, will call native "); + nativeSetPropString(propString); + } + + @Override + public String getPropString() + { + Log.i(TAG, "request getPropString called, will call native "); + return nativeGetPropString(); + } + + + // methods + + @Override + public void funcNoReturnValue(boolean paramBool) { + Log.w(TAG, "request method funcNoReturnValue called, will call native"); + nativeFuncNoReturnValue(paramBool); + } + + @Override + public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { + return CompletableFuture.runAsync( + () -> { funcNoReturnValue(paramBool); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(boolean paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public int funcInt(int paramInt) { + Log.w(TAG, "request method funcInt called, will call native"); + return nativeFuncInt(paramInt); + } + + @Override + public CompletableFuture funcIntAsync(int paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public int funcInt32(int paramInt32) { + Log.w(TAG, "request method funcInt32 called, will call native"); + return nativeFuncInt32(paramInt32); + } + + @Override + public CompletableFuture funcInt32Async(int paramInt32) { + return CompletableFuture.supplyAsync( + () -> {return funcInt32(paramInt32); }, + executor); + } + + @Override + public long funcInt64(long paramInt64) { + Log.w(TAG, "request method funcInt64 called, will call native"); + return nativeFuncInt64(paramInt64); + } + + @Override + public CompletableFuture funcInt64Async(long paramInt64) { + return CompletableFuture.supplyAsync( + () -> {return funcInt64(paramInt64); }, + executor); + } + + @Override + public float funcFloat(float paramFloat) { + Log.w(TAG, "request method funcFloat called, will call native"); + return nativeFuncFloat(paramFloat); + } + + @Override + public CompletableFuture funcFloatAsync(float paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public float funcFloat32(float paramFloat32) { + Log.w(TAG, "request method funcFloat32 called, will call native"); + return nativeFuncFloat32(paramFloat32); + } + + @Override + public CompletableFuture funcFloat32Async(float paramFloat32) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat32(paramFloat32); }, + executor); + } + + @Override + public double funcFloat64(double paramFloat) { + Log.w(TAG, "request method funcFloat64 called, will call native"); + return nativeFuncFloat64(paramFloat); + } + + @Override + public CompletableFuture funcFloat64Async(double paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat64(paramFloat); }, + executor); + } + + @Override + public String funcString(String paramString) { + Log.w(TAG, "request method funcString called, will call native"); + return nativeFuncString(paramString); + } + + @Override + public CompletableFuture funcStringAsync(String paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(boolean propBool); + private native boolean nativeGetPropBool(); + + private native void nativeSetPropInt(int propInt); + private native int nativeGetPropInt(); + + private native void nativeSetPropInt32(int propInt32); + private native int nativeGetPropInt32(); + + private native void nativeSetPropInt64(long propInt64); + private native long nativeGetPropInt64(); + + private native void nativeSetPropFloat(float propFloat); + private native float nativeGetPropFloat(); + + private native void nativeSetPropFloat32(float propFloat32); + private native float nativeGetPropFloat32(); + + private native void nativeSetPropFloat64(double propFloat64); + private native double nativeGetPropFloat64(); + + private native void nativeSetPropString(String propString); + private native String nativeGetPropString(); + + // methods + private native void nativeFuncNoReturnValue(boolean paramBool); + private native boolean nativeFuncBool(boolean paramBool); + private native int nativeFuncInt(int paramInt); + private native int nativeFuncInt32(int paramInt32); + private native long nativeFuncInt64(long paramInt64); + private native float nativeFuncFloat(float paramFloat); + private native float nativeFuncFloat32(float paramFloat32); + private native double nativeFuncFloat64(double paramFloat); + private native String nativeFuncString(String paramString); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(int newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onPropInt32Changed(int newValue) + { + Log.i(TAG, "onPropInt32Changed, will pass notification to all listeners"); + firePropInt32Changed(newValue); + } + public void onPropInt64Changed(long newValue) + { + Log.i(TAG, "onPropInt64Changed, will pass notification to all listeners"); + firePropInt64Changed(newValue); + } + public void onPropFloatChanged(float newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + public void onPropFloat32Changed(float newValue) + { + Log.i(TAG, "onPropFloat32Changed, will pass notification to all listeners"); + firePropFloat32Changed(newValue); + } + public void onPropFloat64Changed(double newValue) + { + Log.i(TAG, "onPropFloat64Changed, will pass notification to all listeners"); + firePropFloat64Changed(newValue); + } + public void onPropStringChanged(String newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(int paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigInt32(int paramInt32) + { + Log.i(TAG, "onSigInt32, will pass notification to all listeners"); + fireSigInt32(paramInt32); + } + public void onSigInt64(long paramInt64) + { + Log.i(TAG, "onSigInt64, will pass notification to all listeners"); + fireSigInt64(paramInt64); + } + public void onSigFloat(float paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigFloat32(float paramFloat32) + { + Log.i(TAG, "onSigFloat32, will pass notification to all listeners"); + fireSigFloat32(paramFloat32); + } + public void onSigFloat64(double paramFloat64) + { + Log.i(TAG, "onSigFloat64, will pass notification to all listeners"); + fireSigFloat64(paramFloat64); + } + public void onSigString(String paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java new file mode 100644 index 0000000..e62ffa0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.ISimpleInterfaceServiceFactory; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_api.AbstractSimpleInterface; +import tbSimple.tbSimplejniservice.SimpleInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SimpleInterfaceJniServiceFactory thread for the system. This is a thread for + * SimpleInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SimpleInterfaceJniServiceFactory extends HandlerThread implements ISimpleInterfaceServiceFactory +{ + private SimpleInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SimpleInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SimpleInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSimpleInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new SimpleInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SimpleInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SimpleInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private SimpleInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SimpleInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SimpleInterfaceJniServiceFactory t = new SimpleInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java new file mode 100644 index 0000000..4fc7a65 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_service.SimpleInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.SimpleInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SimpleInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SimpleInterfaceJniStarter"; + + + + public static ISimpleInterface start(Context context) { + stop(context); + androidService = new Intent(context, SimpleInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SimpleInterfaceJniServiceFactory factory = SimpleInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SimpleInterfaceJniServiceFactory"); + return SimpleInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java new file mode 100644 index 0000000..3a30620 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java @@ -0,0 +1,68 @@ +package tbSimple.tbSimplejniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class VoidInterfaceJniService extends AbstractVoidInterface { + + + private final static String TAG = "VoidInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public VoidInterfaceJniService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.w(TAG, "request method funcVoid called, will call native"); + nativeFuncVoid(); + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + // methods + private native void nativeFuncVoid(); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onSigVoid() + { + Log.i(TAG, "onSigVoid, will pass notification to all listeners"); + fireSigVoid(); + } + +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java new file mode 100644 index 0000000..0ec88e5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbSimple.tbSimplejniservice; + +import tbSimple.tbSimple_android_service.IVoidInterfaceServiceFactory; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_api.AbstractVoidInterface; +import tbSimple.tbSimplejniservice.VoidInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton VoidInterfaceJniServiceFactory thread for the system. This is a thread for + * VoidInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class VoidInterfaceJniServiceFactory extends HandlerThread implements IVoidInterfaceServiceFactory +{ + private VoidInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static VoidInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: VoidInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractVoidInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new VoidInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new VoidInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final VoidInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private VoidInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static VoidInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + VoidInterfaceJniServiceFactory t = new VoidInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java new file mode 100644 index 0000000..87c2a4d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbSimple.tbSimplejniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter; +import tbSimple.tbSimplejniservice.VoidInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class VoidInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "VoidInterfaceJniStarter"; + + + + public static IVoidInterface start(Context context) { + stop(context); + androidService = new Intent(context, VoidInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + VoidInterfaceJniServiceFactory factory = VoidInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for VoidInterfaceJniServiceFactory"); + return VoidInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle b/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle new file mode 100644 index 0000000..3a8bce2 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'tbSimple.tbSimpleserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbSimple.tbSimpleserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbSimple_api') + implementation project(':tbSimple_android_messenger') + implementation project(':tbSimple_android_service') + implementation project(':tbSimple_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..79ad918 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java new file mode 100644 index 0000000..e015a42 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java @@ -0,0 +1,184 @@ +package tbSimple.tbSimpleserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbSimple.tbSimple_android_service.VoidInterfaceServiceAdapter; +import tbSimple.tbSimple_android_service.VoidInterfaceServiceFactory; +import tbSimple.tbSimple_android_service.VoidInterfaceServiceStarter; + +//import message type and parcelabe types + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import java.util.concurrent.CompletableFuture; + + + +public class TbSimpleTestServiceApp extends Activity implements IVoidInterfaceEventListener +{ + + private static final String TAG = "TbSimpleTestServiceApp"; + static Intent stub_service = null; + + + private IVoidInterface mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSigVoid = new Button(this); + bSigVoid.setText("sigVoid"); + + bSigVoid.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sigVoid "); + mBackend.fireSigVoid(); + }); + bSigVoid.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSigVoid); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, VoidInterfaceServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = VoidInterfaceServiceAdapter.setService(VoidInterfaceServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onSigVoid() + { + String text = "Signal sigVoid "; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/colors.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/strings.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..6e0db52 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbSimpleTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/themes.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1.api/Testbed1.java b/goldenmaster/testbed1.api/Testbed1.java deleted file mode 100644 index da52c8d..0000000 --- a/goldenmaster/testbed1.api/Testbed1.java +++ /dev/null @@ -1,255 +0,0 @@ -package testbed1.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class Testbed1 { - - // enumerations - - // data structures - public static class StructBool { - public StructBool(boolean fieldBool) { - this.fieldBool = fieldBool; - } - @JsonProperty("field_bool") - public boolean fieldBool; - } - public static class StructInt { - public StructInt(int fieldInt) { - this.fieldInt = fieldInt; - } - @JsonProperty("field_int") - public int fieldInt; - } - public static class StructFloat { - public StructFloat(float fieldFloat) { - this.fieldFloat = fieldFloat; - } - @JsonProperty("field_float") - public float fieldFloat; - } - public static class StructString { - public StructString(String fieldString) { - this.fieldString = fieldString; - } - @JsonProperty("field_string") - public String fieldString; - } - - // interfaces - public static interface IStructInterfaceEventListener { - void onPropBoolChanged(StructBool oldValue, StructBool newValue); - void onPropIntChanged(StructInt oldValue, StructInt newValue); - void onPropFloatChanged(StructFloat oldValue, StructFloat newValue); - void onPropStringChanged(StructString oldValue, StructString newValue); - void onSigBool(StructBool paramBool); - void onSigInt(StructInt paramInt); - void onSigFloat(StructFloat paramFloat); - void onSigString(StructString paramString); - } - - public static interface IStructInterface { - // properties - void setPropBool(StructBool propBool); - StructBool getPropBool(); - void firePropBoolChanged(StructBool oldValue, StructBool newValue); - - void setPropInt(StructInt propInt); - StructInt getPropInt(); - void firePropIntChanged(StructInt oldValue, StructInt newValue); - - void setPropFloat(StructFloat propFloat); - StructFloat getPropFloat(); - void firePropFloatChanged(StructFloat oldValue, StructFloat newValue); - - void setPropString(StructString propString); - StructString getPropString(); - void firePropStringChanged(StructString oldValue, StructString newValue); - - // methods - StructBool funcBool(StructBool paramBool); - StructInt funcInt(StructInt paramInt); - StructFloat funcFloat(StructFloat paramFloat); - StructString funcString(StructString paramString); - - // signal listeners - void addEventListener(IStructInterfaceEventListener listener); - void removeEventListener(IStructInterfaceEventListener listener); - } - - public static class AbstractStructInterface implements IStructInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void firePropBoolChanged(StructBool oldValue, StructBool newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropBoolChanged(oldValue, newValue); - } - } - - @Override - public void firePropIntChanged(StructInt oldValue, StructInt newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropIntChanged(oldValue, newValue); - } - } - - @Override - public void firePropFloatChanged(StructFloat oldValue, StructFloat newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloatChanged(oldValue, newValue); - } - } - - @Override - public void firePropStringChanged(StructString oldValue, StructString newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropStringChanged(oldValue, newValue); - } - } - - @Override - public void fireSigBool(StructBool paramBool) { - for (ISigBoolEventListener listener : events) { - listener.onSigBool(paramBool); - } - } - - @Override - public void fireSigInt(StructInt paramInt) { - for (ISigIntEventListener listener : events) { - listener.onSigInt(paramInt); - } - } - - @Override - public void fireSigFloat(StructFloat paramFloat) { - for (ISigFloatEventListener listener : events) { - listener.onSigFloat(paramFloat); - } - } - - @Override - public void fireSigString(StructString paramString) { - for (ISigStringEventListener listener : events) { - listener.onSigString(paramString); - } - } - - - } - public static interface IStructArrayInterfaceEventListener { - void onPropBoolChanged(StructBool[] oldValue, StructBool[] newValue); - void onPropIntChanged(StructInt[] oldValue, StructInt[] newValue); - void onPropFloatChanged(StructFloat[] oldValue, StructFloat[] newValue); - void onPropStringChanged(StructString[] oldValue, StructString[] newValue); - void onSigBool(StructBool[] paramBool); - void onSigInt(StructInt[] paramInt); - void onSigFloat(StructFloat[] paramFloat); - void onSigString(StructString[] paramString); - } - - public static interface IStructArrayInterface { - // properties - void setPropBool(StructBool[] propBool); - StructBool[] getPropBool(); - void firePropBoolChanged(StructBool[] oldValue, StructBool[] newValue); - - void setPropInt(StructInt[] propInt); - StructInt[] getPropInt(); - void firePropIntChanged(StructInt[] oldValue, StructInt[] newValue); - - void setPropFloat(StructFloat[] propFloat); - StructFloat[] getPropFloat(); - void firePropFloatChanged(StructFloat[] oldValue, StructFloat[] newValue); - - void setPropString(StructString[] propString); - StructString[] getPropString(); - void firePropStringChanged(StructString[] oldValue, StructString[] newValue); - - // methods - StructBool[] funcBool(StructBool[] paramBool); - StructInt[] funcInt(StructInt[] paramInt); - StructFloat[] funcFloat(StructFloat[] paramFloat); - StructString[] funcString(StructString[] paramString); - - // signal listeners - void addEventListener(IStructArrayInterfaceEventListener listener); - void removeEventListener(IStructArrayInterfaceEventListener listener); - } - - public static class AbstractStructArrayInterface implements IStructArrayInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void firePropBoolChanged(StructBool[] oldValue, StructBool[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropBoolChanged(oldValue, newValue); - } - } - - @Override - public void firePropIntChanged(StructInt[] oldValue, StructInt[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropIntChanged(oldValue, newValue); - } - } - - @Override - public void firePropFloatChanged(StructFloat[] oldValue, StructFloat[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropFloatChanged(oldValue, newValue); - } - } - - @Override - public void firePropStringChanged(StructString[] oldValue, StructString[] newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onPropStringChanged(oldValue, newValue); - } - } - - @Override - public void fireSigBool(StructBool[] paramBool) { - for (ISigBoolEventListener listener : events) { - listener.onSigBool(paramBool); - } - } - - @Override - public void fireSigInt(StructInt[] paramInt) { - for (ISigIntEventListener listener : events) { - listener.onSigInt(paramInt); - } - } - - @Override - public void fireSigFloat(StructFloat[] paramFloat) { - for (ISigFloatEventListener listener : events) { - listener.onSigFloat(paramFloat); - } - } - - @Override - public void fireSigString(StructString[] paramString) { - for (ISigStringEventListener listener : events) { - listener.onSigString(paramString); - } - } - - - } -} \ No newline at end of file diff --git a/goldenmaster/testbed1/gradle.properties b/goldenmaster/testbed1/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/testbed1/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/testbed1/gradle/libs.versions.toml b/goldenmaster/testbed1/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/testbed1/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/testbed1/settings.gradle b/goldenmaster/testbed1/settings.gradle new file mode 100644 index 0000000..8dd3315 --- /dev/null +++ b/goldenmaster/testbed1/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "testbed1" +include ':testbed1_android_service' +include ':testbed1_android_client' +include ':testbed1_android_messenger' +include ':testbed1_impl' +include ':testbed1_api' +include ':testbed1_client_example' +include ':testbed1serviceexample' \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_android_client/additions.gradle b/goldenmaster/testbed1/testbed1_android_client/additions.gradle new file mode 100644 index 0000000..b37559e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'testbed1.testbed1_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + implementation project(':testbed1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_android_client/build.gradle b/goldenmaster/testbed1/testbed1_android_client/build.gradle new file mode 100644 index 0000000..8ebcace --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed1" +version = "1.0.0" + +android { + namespace 'testbed1.testbed1_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + implementation project(':testbed1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java new file mode 100644 index 0000000..8f87b1c --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java @@ -0,0 +1,717 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class StructArrayInterfaceClient extends AbstractStructArrayInterface implements ServiceConnection +{ + private static final String TAG = "StructArrayInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private StructBool[] m_propBool = new StructBool[]{}; + private StructInt[] m_propInt = new StructInt[]{}; + private StructFloat[] m_propFloat = new StructFloat[]{}; + private StructString[] m_propString = new StructString[]{}; + + + public StructArrayInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type StructArrayInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (StructArrayInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + + StructBool[] propBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); + onPropBool(propBool); + + StructInt[] propInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); + onPropInt(propInt); + + StructFloat[] propFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); + onPropFloat(propFloat); + + StructString[] propString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); + onPropString(propString); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + + StructBool[] propBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + + StructInt[] propInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); + + onPropInt(propInt); + break; + } + case SET_PropFloat: + { + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + + StructFloat[] propFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); + + onPropFloat(propFloat); + break; + } + case SET_PropString: + { + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + + StructString[] propString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); + + onPropString(propString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigBool: { + + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool[] paramBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); + onSigBool(paramBool); + break; + } + case SIG_SigInt: { + + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt[] paramInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); + onSigInt(paramInt); + break; + } + case SIG_SigFloat: { + + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat[] paramFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); + onSigFloat(paramFloat); + break; + } + case SIG_SigString: { + + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString[] paramString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); + onSigString(paramString); + break; + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArrayInterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncIntResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArrayInterfaceMessageType.RPC_FuncIntResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloatResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArrayInterfaceMessageType.RPC_FuncFloatResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncStringResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArrayInterfaceMessageType.RPC_FuncStringResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(StructBool[] propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (! Arrays.equals(m_propBool, propBool)) + { + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propBool", StructBoolParcelable.wrapArray(propBool)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(StructBool[] propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (! Arrays.equals(m_propBool, propBool)) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public StructBool[] getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(StructInt[] propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (! Arrays.equals(m_propInt, propInt)) + { + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propInt", StructIntParcelable.wrapArray(propInt)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(StructInt[] propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (! Arrays.equals(m_propInt, propInt)) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public StructInt[] getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + @Override + public void setPropFloat(StructFloat[] propFloat) + { + Log.i(TAG, "request setPropFloat called "+ propFloat); + if (! Arrays.equals(m_propFloat, propFloat)) + { + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.PROP_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propFloat", StructFloatParcelable.wrapArray(propFloat)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat(StructFloat[] propFloat) + { + Log.i(TAG, "value received from service for PropFloat "); + if (! Arrays.equals(m_propFloat, propFloat)) + { + m_propFloat = propFloat; + firePropFloatChanged(propFloat); + } + + } + + @Override + public StructFloat[] getPropFloat() + { + Log.i(TAG, "request getPropFloat called, returning local"); + return m_propFloat; + } + + + @Override + public void setPropString(StructString[] propString) + { + Log.i(TAG, "request setPropString called "+ propString); + if (! Arrays.equals(m_propString, propString)) + { + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.PROP_PropString.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propString", StructStringParcelable.wrapArray(propString)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropString(StructString[] propString) + { + Log.i(TAG, "value received from service for PropString "); + if (! Arrays.equals(m_propString, propString)) + { + m_propString = propString; + firePropStringChanged(propString); + } + + } + + @Override + public StructString[] getPropString() + { + Log.i(TAG, "request getPropString called, returning local"); + return m_propString; + } + + + // methods + + + @Override + public StructBool[] funcBool(StructBool[] paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(StructBool[] paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("paramBool", StructBoolParcelable.wrapArray(paramBool)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructBool[] result = StructBoolParcelable.unwrapArray((StructBoolParcelable[])bundle.getParcelableArray("result", StructBoolParcelable.class)); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructInt[] funcInt(StructInt[] paramInt) { + CompletableFuture resFuture = funcIntAsync(paramInt); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcIntAsync(StructInt[] paramInt) { + + Log.i(TAG, "Call on service funcInt "+ " " + paramInt); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.RPC_FuncIntReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("paramInt", StructIntParcelable.wrapArray(paramInt)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructInt[] result = StructIntParcelable.unwrapArray((StructIntParcelable[])bundle.getParcelableArray("result", StructIntParcelable.class)); + Log.v(TAG, "resolve funcInt" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructFloat[] funcFloat(StructFloat[] paramFloat) { + CompletableFuture resFuture = funcFloatAsync(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloatAsync(StructFloat[] paramFloat) { + + Log.i(TAG, "Call on service funcFloat "+ " " + paramFloat); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.RPC_FuncFloatReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("paramFloat", StructFloatParcelable.wrapArray(paramFloat)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructFloat[] result = StructFloatParcelable.unwrapArray((StructFloatParcelable[])bundle.getParcelableArray("result", StructFloatParcelable.class)); + Log.v(TAG, "resolve funcFloat" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructString[] funcString(StructString[] paramString) { + CompletableFuture resFuture = funcStringAsync(paramString); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcStringAsync(StructString[] paramString) { + + Log.i(TAG, "Call on service funcString "+ " " + paramString); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.RPC_FuncStringReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("paramString", StructStringParcelable.wrapArray(paramString)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructString[] result = StructStringParcelable.unwrapArray((StructStringParcelable[])bundle.getParcelableArray("result", StructStringParcelable.class)); + Log.v(TAG, "resolve funcString" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigBool(StructBool[] paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } + public void onSigInt(StructInt[] paramInt) + { + Log.i(TAG, "onSigInt received from service"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloat[] paramFloat) + { + Log.i(TAG, "onSigFloat received from service"); + fireSigFloat(paramFloat); + } + public void onSigString(StructString[] paramString) + { + Log.i(TAG, "onSigString received from service"); + fireSigString(paramString); + } +} diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java new file mode 100644 index 0000000..b381158 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java @@ -0,0 +1,717 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class StructInterfaceClient extends AbstractStructInterface implements ServiceConnection +{ + private static final String TAG = "StructInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private StructBool m_propBool = new StructBool(); + private StructInt m_propInt = new StructInt(); + private StructFloat m_propFloat = new StructFloat(); + private StructString m_propString = new StructString(); + + + public StructInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type StructInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed1.testbed1_android_service.StructInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, StructInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, StructInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (StructInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + + StructBool propBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); + onPropBool(propBool); + + StructInt propInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); + onPropInt(propInt); + + StructFloat propFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); + onPropFloat(propFloat); + + StructString propString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); + onPropString(propString); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + + StructBool propBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + + StructInt propInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); + + onPropInt(propInt); + break; + } + case SET_PropFloat: + { + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + + StructFloat propFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); + + onPropFloat(propFloat); + break; + } + case SET_PropString: + { + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + + StructString propString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); + + onPropString(propString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigBool: { + + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool paramBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); + onSigBool(paramBool); + break; + } + case SIG_SigInt: { + + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt paramInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); + onSigInt(paramInt); + break; + } + case SIG_SigFloat: { + + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat paramFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); + onSigFloat(paramFloat); + break; + } + case SIG_SigString: { + + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString paramString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); + onSigString(paramString); + break; + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructInterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncIntResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructInterfaceMessageType.RPC_FuncIntResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloatResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructInterfaceMessageType.RPC_FuncFloatResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncStringResp: { + + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructInterfaceMessageType.RPC_FuncStringResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(StructBool propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (! m_propBool.equals(propBool)) + { + Message msg = new Message(); + msg.what = StructInterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propBool", new StructBoolParcelable(propBool)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(StructBool propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (! m_propBool.equals(propBool)) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public StructBool getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(StructInt propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (! m_propInt.equals(propInt)) + { + Message msg = new Message(); + msg.what = StructInterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propInt", new StructIntParcelable(propInt)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(StructInt propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (! m_propInt.equals(propInt)) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public StructInt getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + @Override + public void setPropFloat(StructFloat propFloat) + { + Log.i(TAG, "request setPropFloat called "+ propFloat); + if (! m_propFloat.equals(propFloat)) + { + Message msg = new Message(); + msg.what = StructInterfaceMessageType.PROP_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propFloat", new StructFloatParcelable(propFloat)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat(StructFloat propFloat) + { + Log.i(TAG, "value received from service for PropFloat "); + if (! m_propFloat.equals(propFloat)) + { + m_propFloat = propFloat; + firePropFloatChanged(propFloat); + } + + } + + @Override + public StructFloat getPropFloat() + { + Log.i(TAG, "request getPropFloat called, returning local"); + return m_propFloat; + } + + + @Override + public void setPropString(StructString propString) + { + Log.i(TAG, "request setPropString called "+ propString); + if (! m_propString.equals(propString)) + { + Message msg = new Message(); + msg.what = StructInterfaceMessageType.PROP_PropString.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propString", new StructStringParcelable(propString)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropString(StructString propString) + { + Log.i(TAG, "value received from service for PropString "); + if (! m_propString.equals(propString)) + { + m_propString = propString; + firePropStringChanged(propString); + } + + } + + @Override + public StructString getPropString() + { + Log.i(TAG, "request getPropString called, returning local"); + return m_propString; + } + + + // methods + + + @Override + public StructBool funcBool(StructBool paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(StructBool paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramBool", new StructBoolParcelable(paramBool)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructBool result = bundle.getParcelable("result", StructBoolParcelable.class).getStructBool(); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructInt funcInt(StructInt paramInt) { + CompletableFuture resFuture = funcIntAsync(paramInt); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcIntAsync(StructInt paramInt) { + + Log.i(TAG, "Call on service funcInt "+ " " + paramInt); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.RPC_FuncIntReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramInt", new StructIntParcelable(paramInt)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructInt result = bundle.getParcelable("result", StructIntParcelable.class).getStructInt(); + Log.v(TAG, "resolve funcInt" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructFloat funcFloat(StructFloat paramFloat) { + CompletableFuture resFuture = funcFloatAsync(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloatAsync(StructFloat paramFloat) { + + Log.i(TAG, "Call on service funcFloat "+ " " + paramFloat); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.RPC_FuncFloatReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramFloat", new StructFloatParcelable(paramFloat)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructFloat result = bundle.getParcelable("result", StructFloatParcelable.class).getStructFloat(); + Log.v(TAG, "resolve funcFloat" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructString funcString(StructString paramString) { + CompletableFuture resFuture = funcStringAsync(paramString); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcStringAsync(StructString paramString) { + + Log.i(TAG, "Call on service funcString "+ " " + paramString); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.RPC_FuncStringReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramString", new StructStringParcelable(paramString)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructString result = bundle.getParcelable("result", StructStringParcelable.class).getStructString(); + Log.v(TAG, "resolve funcString" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigBool(StructBool paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } + public void onSigInt(StructInt paramInt) + { + Log.i(TAG, "onSigInt received from service"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloat paramFloat) + { + Log.i(TAG, "onSigFloat received from service"); + fireSigFloat(paramFloat); + } + public void onSigString(StructString paramString) + { + Log.i(TAG, "onSigString received from service"); + fireSigString(paramString); + } +} diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java new file mode 100644 index 0000000..4abc19e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java @@ -0,0 +1,566 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed1.testbed1_android_client; + +import testbed1.testbed1_android_client.StructArrayInterfaceClient; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.Testbed1TestHelper; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IStructArrayInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class StructArrayInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private StructArrayInterfaceClient testedClient; + private IStructArrayInterfaceEventListener listenerMock = mock(IStructArrayInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IStructArrayInterfaceClientMessageGetter serviceMessagesStorage = mock(IStructArrayInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IStructArrayInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new StructArrayInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed1.testbed1_android_service", "testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + StructBool[] testpropBool = new StructBool[1]; + testpropBool[0] = Testbed1TestHelper.makeTestStructBool(); + data.putParcelableArray("propBool", StructBoolParcelable.wrapArray(testpropBool)); + StructInt[] testpropInt = new StructInt[1]; + testpropInt[0] = Testbed1TestHelper.makeTestStructInt(); + data.putParcelableArray("propInt", StructIntParcelable.wrapArray(testpropInt)); + StructFloat[] testpropFloat = new StructFloat[1]; + testpropFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelableArray("propFloat", StructFloatParcelable.wrapArray(testpropFloat)); + StructString[] testpropString = new StructString[1]; + testpropString[0] = Testbed1TestHelper.makeTestStructString(); + data.putParcelableArray("propString", StructStringParcelable.wrapArray(testpropString)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBool[].class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt[].class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat[].class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString[].class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + StructBool[] testpropBool = new StructBool[1]; + testpropBool[0] = Testbed1TestHelper.makeTestStructBool(); + data.putParcelableArray("propBool", StructBoolParcelable.wrapArray(testpropBool)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBool[].class)); + } + + @Test + public void setPropertyRequestpropBool() + { + StructBool[] testpropBool = new StructBool[1]; + testpropBool[0] = Testbed1TestHelper.makeTestStructBool(); + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool[] receivedpropBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + StructInt[] testpropInt = new StructInt[1]; + testpropInt[0] = Testbed1TestHelper.makeTestStructInt(); + data.putParcelableArray("propInt", StructIntParcelable.wrapArray(testpropInt)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt[].class)); + } + + @Test + public void setPropertyRequestpropInt() + { + StructInt[] testpropInt = new StructInt[1]; + testpropInt[0] = Testbed1TestHelper.makeTestStructInt(); + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt[] receivedpropInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SET_PropFloat.getValue()); + Bundle data = new Bundle(); + StructFloat[] testpropFloat = new StructFloat[1]; + testpropFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelableArray("propFloat", StructFloatParcelable.wrapArray(testpropFloat)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat[].class)); + } + + @Test + public void setPropertyRequestpropFloat() + { + StructFloat[] testpropFloat = new StructFloat[1]; + testpropFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + + testedClient.setPropFloat(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.PROP_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat[] receivedpropFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); + assertEquals(receivedpropFloat, testpropFloat); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SET_PropString.getValue()); + Bundle data = new Bundle(); + StructString[] testpropString = new StructString[1]; + testpropString[0] = Testbed1TestHelper.makeTestStructString(); + data.putParcelableArray("propString", StructStringParcelable.wrapArray(testpropString)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString[].class)); + } + + @Test + public void setPropertyRequestpropString() + { + StructString[] testpropString = new StructString[1]; + testpropString[0] = Testbed1TestHelper.makeTestStructString(); + + testedClient.setPropString(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.PROP_PropString.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString[] receivedpropString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); + assertEquals(receivedpropString, testpropString); + } + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + StructBool[] testparamBool = new StructBool[1]; + testparamBool[0] = Testbed1TestHelper.makeTestStructBool(); + data.putParcelableArray("paramBool", StructBoolParcelable.wrapArray(testparamBool)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool( any(StructBool[].class)); + +} + @Test + public void whenNotifiedsigInt() throws RemoteException + { + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SIG_SigInt.getValue()); + Bundle data = new Bundle(); + StructInt[] testparamInt = new StructInt[1]; + testparamInt[0] = Testbed1TestHelper.makeTestStructInt(); + data.putParcelableArray("paramInt", StructIntParcelable.wrapArray(testparamInt)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt( any(StructInt[].class)); + +} + @Test + public void whenNotifiedsigFloat() throws RemoteException + { + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SIG_SigFloat.getValue()); + Bundle data = new Bundle(); + StructFloat[] testparamFloat = new StructFloat[1]; + testparamFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelableArray("paramFloat", StructFloatParcelable.wrapArray(testparamFloat)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat( any(StructFloat[].class)); + +} + @Test + public void whenNotifiedsigString() throws RemoteException + { + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SIG_SigString.getValue()); + Bundle data = new Bundle(); + StructString[] testparamString = new StructString[1]; + testparamString[0] = Testbed1TestHelper.makeTestStructString(); + data.putParcelableArray("paramString", StructStringParcelable.wrapArray(testparamString)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigString( any(StructString[].class)); + +} + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + StructBool[] testparamBool = new StructBool[1]; + testparamBool[0] = Testbed1TestHelper.makeTestStructBool(); + StructBool[] expectedResult = new StructBool[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructBool(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool[] receivedparamBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructBoolParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncIntRequest() throws RemoteException { + + // Execute method + StructInt[] testparamInt = new StructInt[1]; + testparamInt[0] = Testbed1TestHelper.makeTestStructInt(); + StructInt[] expectedResult = new StructInt[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructInt(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcIntAsync(testparamInt); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt[] receivedparamInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); + assertEquals(receivedparamInt, testparamInt); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncIntResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructIntParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloatRequest() throws RemoteException { + + // Execute method + StructFloat[] testparamFloat = new StructFloat[1]; + testparamFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + StructFloat[] expectedResult = new StructFloat[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructFloat(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloatAsync(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat[] receivedparamFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); + assertEquals(receivedparamFloat, testparamFloat); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncFloatResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructFloatParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncStringRequest() throws RemoteException { + + // Execute method + StructString[] testparamString = new StructString[1]; + testparamString[0] = Testbed1TestHelper.makeTestStructString(); + StructString[] expectedResult = new StructString[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructString(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcStringAsync(testparamString); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString[] receivedparamString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); + assertEquals(receivedparamString, testparamString); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncStringResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructStringParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java new file mode 100644 index 0000000..8d9ec4f --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java @@ -0,0 +1,542 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed1.testbed1_android_client; + +import testbed1.testbed1_android_client.StructInterfaceClient; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.Testbed1TestHelper; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IStructInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class StructInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private StructInterfaceClient testedClient; + private IStructInterfaceEventListener listenerMock = mock(IStructInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IStructInterfaceClientMessageGetter serviceMessagesStorage = mock(IStructInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(StructInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IStructInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new StructInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed1.testbed1_android_service", "testbed1.testbed1_android_service.StructInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(StructInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, StructInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + StructBool testpropBool = Testbed1TestHelper.makeTestStructBool(); + data.putParcelable("propBool", new StructBoolParcelable(testpropBool)); + StructInt testpropInt = Testbed1TestHelper.makeTestStructInt(); + data.putParcelable("propInt", new StructIntParcelable(testpropInt)); + StructFloat testpropFloat = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelable("propFloat", new StructFloatParcelable(testpropFloat)); + StructString testpropString = Testbed1TestHelper.makeTestStructString(); + data.putParcelable("propString", new StructStringParcelable(testpropString)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBool.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + StructBool testpropBool = Testbed1TestHelper.makeTestStructBool(); + data.putParcelable("propBool", new StructBoolParcelable(testpropBool)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBool.class)); + } + + @Test + public void setPropertyRequestpropBool() + { + StructBool testpropBool = Testbed1TestHelper.makeTestStructBool(); + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool receivedpropBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + StructInt testpropInt = Testbed1TestHelper.makeTestStructInt(); + data.putParcelable("propInt", new StructIntParcelable(testpropInt)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt.class)); + } + + @Test + public void setPropertyRequestpropInt() + { + StructInt testpropInt = Testbed1TestHelper.makeTestStructInt(); + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt receivedpropInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.SET_PropFloat.getValue()); + Bundle data = new Bundle(); + StructFloat testpropFloat = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelable("propFloat", new StructFloatParcelable(testpropFloat)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat.class)); + } + + @Test + public void setPropertyRequestpropFloat() + { + StructFloat testpropFloat = Testbed1TestHelper.makeTestStructFloat(); + + testedClient.setPropFloat(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.PROP_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat receivedpropFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); + assertEquals(receivedpropFloat, testpropFloat); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.SET_PropString.getValue()); + Bundle data = new Bundle(); + StructString testpropString = Testbed1TestHelper.makeTestStructString(); + data.putParcelable("propString", new StructStringParcelable(testpropString)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString.class)); + } + + @Test + public void setPropertyRequestpropString() + { + StructString testpropString = Testbed1TestHelper.makeTestStructString(); + + testedClient.setPropString(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.PROP_PropString.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString receivedpropString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); + assertEquals(receivedpropString, testpropString); + } + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, StructInterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + StructBool testparamBool = Testbed1TestHelper.makeTestStructBool(); + data.putParcelable("paramBool", new StructBoolParcelable(testparamBool)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool( any(StructBool.class)); + +} + @Test + public void whenNotifiedsigInt() throws RemoteException + { + + Message msg = Message.obtain(null, StructInterfaceMessageType.SIG_SigInt.getValue()); + Bundle data = new Bundle(); + StructInt testparamInt = Testbed1TestHelper.makeTestStructInt(); + data.putParcelable("paramInt", new StructIntParcelable(testparamInt)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt( any(StructInt.class)); + +} + @Test + public void whenNotifiedsigFloat() throws RemoteException + { + + Message msg = Message.obtain(null, StructInterfaceMessageType.SIG_SigFloat.getValue()); + Bundle data = new Bundle(); + StructFloat testparamFloat = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelable("paramFloat", new StructFloatParcelable(testparamFloat)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat( any(StructFloat.class)); + +} + @Test + public void whenNotifiedsigString() throws RemoteException + { + + Message msg = Message.obtain(null, StructInterfaceMessageType.SIG_SigString.getValue()); + Bundle data = new Bundle(); + StructString testparamString = Testbed1TestHelper.makeTestStructString(); + data.putParcelable("paramString", new StructStringParcelable(testparamString)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigString( any(StructString.class)); + +} + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + StructBool testparamBool = Testbed1TestHelper.makeTestStructBool(); + StructBool expectedResult = Testbed1TestHelper.makeTestStructBool(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool receivedparamBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new StructBoolParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncIntRequest() throws RemoteException { + + // Execute method + StructInt testparamInt = Testbed1TestHelper.makeTestStructInt(); + StructInt expectedResult = Testbed1TestHelper.makeTestStructInt(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcIntAsync(testparamInt); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt receivedparamInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); + assertEquals(receivedparamInt, testparamInt); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncIntResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new StructIntParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloatRequest() throws RemoteException { + + // Execute method + StructFloat testparamFloat = Testbed1TestHelper.makeTestStructFloat(); + StructFloat expectedResult = Testbed1TestHelper.makeTestStructFloat(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloatAsync(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat receivedparamFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); + assertEquals(receivedparamFloat, testparamFloat); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncFloatResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new StructFloatParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncStringRequest() throws RemoteException { + + // Execute method + StructString testparamString = Testbed1TestHelper.makeTestStructString(); + StructString expectedResult = Testbed1TestHelper.makeTestStructString(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcStringAsync(testparamString); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString receivedparamString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); + assertEquals(receivedparamString, testparamString); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncStringResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new StructStringParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed1/testbed1_android_messenger/additions.gradle b/goldenmaster/testbed1/testbed1_android_messenger/additions.gradle new file mode 100644 index 0000000..a9d6b05 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'testbed1.testbed1_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_android_messenger/build.gradle b/goldenmaster/testbed1/testbed1_android_messenger/build.gradle new file mode 100644 index 0000000..103cd93 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed1" +version = "1.0.0" + +android { + namespace 'testbed1.testbed1_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java new file mode 100644 index 0000000..4be27cb --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java @@ -0,0 +1,50 @@ +package testbed1.testbed1_android_messenger; + +public enum StructArrayInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + PROP_PropFloat(7), + SET_PropFloat(8), + PROP_PropString(9), + SET_PropString(10), + SIG_SigBool(11), + SIG_SigInt(12), + SIG_SigFloat(13), + SIG_SigString(14), + RPC_FuncBoolReq(15), + RPC_FuncBoolResp(16), + RPC_FuncIntReq(17), + RPC_FuncIntResp(18), + RPC_FuncFloatReq(19), + RPC_FuncFloatResp(20), + RPC_FuncStringReq(21), + RPC_FuncStringResp(22), + StructArrayInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + StructArrayInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static StructArrayInterfaceMessageType fromInteger(int value) + { + for (StructArrayInterfaceMessageType event : StructArrayInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return StructArrayInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolParcelable.java new file mode 100644 index 0000000..3ba072e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructBool; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructBoolParcelable implements Parcelable { + + public StructBool data; + + public StructBoolParcelable(StructBool data) { + this.data = new StructBool(data); + } + + public StructBool getStructBool() + { + return new StructBool(data); + } + + protected StructBoolParcelable(Parcel in) { + this.data = new StructBool(); + data.fieldBool = in.readBoolean(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructBoolParcelable createFromParcel(Parcel in) { + return new StructBoolParcelable(in); + } + + @Override + public StructBoolParcelable[] newArray(int size) { + return new StructBoolParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(data.fieldBool); + + + } + public static StructBoolParcelable[] wrapArray(StructBool[] structs) { + if (structs == null) return null; + StructBoolParcelable[] out = new StructBoolParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructBoolParcelable(structs[i]); + } + return out; + } + + public static StructBool[] unwrapArray(StructBoolParcelable[] parcelables) { + if (parcelables == null) return null; + StructBool[] out = new StructBool[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructBool(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatParcelable.java new file mode 100644 index 0000000..23bc5f4 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructFloat; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructFloatParcelable implements Parcelable { + + public StructFloat data; + + public StructFloatParcelable(StructFloat data) { + this.data = new StructFloat(data); + } + + public StructFloat getStructFloat() + { + return new StructFloat(data); + } + + protected StructFloatParcelable(Parcel in) { + this.data = new StructFloat(); + data.fieldFloat = in.readFloat(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructFloatParcelable createFromParcel(Parcel in) { + return new StructFloatParcelable(in); + } + + @Override + public StructFloatParcelable[] newArray(int size) { + return new StructFloatParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(data.fieldFloat); + + + } + public static StructFloatParcelable[] wrapArray(StructFloat[] structs) { + if (structs == null) return null; + StructFloatParcelable[] out = new StructFloatParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructFloatParcelable(structs[i]); + } + return out; + } + + public static StructFloat[] unwrapArray(StructFloatParcelable[] parcelables) { + if (parcelables == null) return null; + StructFloat[] out = new StructFloat[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructFloat(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntParcelable.java new file mode 100644 index 0000000..bc2fdd9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructInt; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructIntParcelable implements Parcelable { + + public StructInt data; + + public StructIntParcelable(StructInt data) { + this.data = new StructInt(data); + } + + public StructInt getStructInt() + { + return new StructInt(data); + } + + protected StructIntParcelable(Parcel in) { + this.data = new StructInt(); + data.fieldInt = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructIntParcelable createFromParcel(Parcel in) { + return new StructIntParcelable(in); + } + + @Override + public StructIntParcelable[] newArray(int size) { + return new StructIntParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.fieldInt); + + + } + public static StructIntParcelable[] wrapArray(StructInt[] structs) { + if (structs == null) return null; + StructIntParcelable[] out = new StructIntParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructIntParcelable(structs[i]); + } + return out; + } + + public static StructInt[] unwrapArray(StructIntParcelable[] parcelables) { + if (parcelables == null) return null; + StructInt[] out = new StructInt[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructInt(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceMessageType.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceMessageType.java new file mode 100644 index 0000000..39dba89 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceMessageType.java @@ -0,0 +1,50 @@ +package testbed1.testbed1_android_messenger; + +public enum StructInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + PROP_PropFloat(7), + SET_PropFloat(8), + PROP_PropString(9), + SET_PropString(10), + SIG_SigBool(11), + SIG_SigInt(12), + SIG_SigFloat(13), + SIG_SigString(14), + RPC_FuncBoolReq(15), + RPC_FuncBoolResp(16), + RPC_FuncIntReq(17), + RPC_FuncIntResp(18), + RPC_FuncFloatReq(19), + RPC_FuncFloatResp(20), + RPC_FuncStringReq(21), + RPC_FuncStringResp(22), + StructInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + StructInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static StructInterfaceMessageType fromInteger(int value) + { + for (StructInterfaceMessageType event : StructInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return StructInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringParcelable.java new file mode 100644 index 0000000..ea8d5b4 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructString; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructStringParcelable implements Parcelable { + + public StructString data; + + public StructStringParcelable(StructString data) { + this.data = new StructString(data); + } + + public StructString getStructString() + { + return new StructString(data); + } + + protected StructStringParcelable(Parcel in) { + this.data = new StructString(); + data.fieldString = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructStringParcelable createFromParcel(Parcel in) { + return new StructStringParcelable(in); + } + + @Override + public StructStringParcelable[] newArray(int size) { + return new StructStringParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(data.fieldString); + + + } + public static StructStringParcelable[] wrapArray(StructString[] structs) { + if (structs == null) return null; + StructStringParcelable[] out = new StructStringParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructStringParcelable(structs[i]); + } + return out; + } + + public static StructString[] unwrapArray(StructStringParcelable[] parcelables) { + if (parcelables == null) return null; + StructString[] out = new StructString[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructString(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_service/additions.gradle b/goldenmaster/testbed1/testbed1_android_service/additions.gradle new file mode 100644 index 0000000..19cf4ac --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'testbed1.testbed1_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + implementation project(':testbed1_impl') + implementation project(':testbed1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_android_service/build.gradle b/goldenmaster/testbed1/testbed1_android_service/build.gradle new file mode 100644 index 0000000..a7f6d9e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed1" +version = "1.0.0" + + +android { + namespace 'testbed1.testbed1_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + implementation project(':testbed1_impl') + implementation project(':testbed1_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml b/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..bfd5dda --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArrayInterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArrayInterfaceServiceFactory.java new file mode 100644 index 0000000..597c3a4 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArrayInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed1.testbed1_android_service; +import testbed1.testbed1_api.IStructArrayInterface; + + +public interface IStructArrayInterfaceServiceFactory { + public IStructArrayInterface getServiceInstance(); +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructInterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructInterfaceServiceFactory.java new file mode 100644 index 0000000..787ffd6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed1.testbed1_android_service; +import testbed1.testbed1_api.IStructInterface; + + +public interface IStructInterfaceServiceFactory { + public IStructInterface getServiceInstance(); +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java new file mode 100644 index 0000000..8f2bfae --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java @@ -0,0 +1,492 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_android_service.IStructArrayInterfaceServiceFactory; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class StructArrayInterfaceServiceAdapter extends Service +{ + private static final String TAG = "StructArrayInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IStructArrayInterface mBackendService; + private static IStructArrayInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public StructArrayInterfaceServiceAdapter() + { + } + + public static IStructArrayInterface setService(IStructArrayInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(StructArrayInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: StructArrayInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(StructArrayInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(StructArrayInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IStructArrayInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (StructArrayInterfaceMessageType.fromInteger(msg.what) != StructArrayInterfaceMessageType.REGISTER_CLIENT + && StructArrayInterfaceMessageType.fromInteger(msg.what) != StructArrayInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: StructArrayInterfaceMessageType" + StructArrayInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (StructArrayInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool[] propBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt[] propInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); + mBackendService.setPropInt(propInt); + break; + } + case PROP_PropFloat: + { + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat[] propFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); + mBackendService.setPropFloat(propFloat); + break; + } + case PROP_PropString: + { + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString[] propString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); + mBackendService.setPropString(propString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructBool[] paramBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); + + StructBool[] result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = StructArrayInterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructBoolParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncIntReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructInt[] paramInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); + + StructInt[] result = mBackendService.funcInt(paramInt); + + Message respMsg = new Message(); + respMsg.what = StructArrayInterfaceMessageType.RPC_FuncIntResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructIntParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloatReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructFloat[] paramFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); + + StructFloat[] result = mBackendService.funcFloat(paramFloat); + + Message respMsg = new Message(); + respMsg.what = StructArrayInterfaceMessageType.RPC_FuncFloatResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructFloatParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncStringReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructString[] paramString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); + + StructString[] result = mBackendService.funcString(paramString); + + Message respMsg = new Message(); + respMsg.what = StructArrayInterfaceMessageType.RPC_FuncStringResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructStringParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + StructBool[] propBool = mBackendService.getPropBool(); + + data.putParcelableArray("propBool", StructBoolParcelable.wrapArray(propBool)); + StructInt[] propInt = mBackendService.getPropInt(); + + data.putParcelableArray("propInt", StructIntParcelable.wrapArray(propInt)); + StructFloat[] propFloat = mBackendService.getPropFloat(); + + data.putParcelableArray("propFloat", StructFloatParcelable.wrapArray(propFloat)); + StructString[] propString = mBackendService.getPropString(); + + data.putParcelableArray("propString", StructStringParcelable.wrapArray(propString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(StructBool[] propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propBool", StructBoolParcelable.wrapArray(propBool)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(StructInt[] propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propInt", StructIntParcelable.wrapArray(propInt)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloatChanged(StructFloat[] propFloat){ + Log.i(TAG, "New value for PropFloat from backend" + propFloat); + + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SET_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propFloat", StructFloatParcelable.wrapArray(propFloat)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropStringChanged(StructString[] propString){ + Log.i(TAG, "New value for PropString from backend" + propString); + + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SET_PropString.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propString", StructStringParcelable.wrapArray(propString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(StructBool[] paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("paramBool", StructBoolParcelable.wrapArray(paramBool)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt(StructInt[] paramInt){ + Log.i(TAG, "New singal for SigInt = "+ " " + paramInt); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SIG_SigInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("paramInt", StructIntParcelable.wrapArray(paramInt)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat(StructFloat[] paramFloat){ + Log.i(TAG, "New singal for SigFloat = "+ " " + paramFloat); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SIG_SigFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("paramFloat", StructFloatParcelable.wrapArray(paramFloat)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigString(StructString[] paramString){ + Log.i(TAG, "New singal for SigString = "+ " " + paramString); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SIG_SigString.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("paramString", StructStringParcelable.wrapArray(paramString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java new file mode 100644 index 0000000..c36fccd --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import testbed1.testbed1_android_service.IStructArrayInterfaceServiceFactory; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton StructArrayInterfaceServiceFactory thread for the system. This is a thread for + * StructArrayInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class StructArrayInterfaceServiceFactory extends HandlerThread implements IStructArrayInterfaceServiceFactory +{ + private StructArrayInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static StructArrayInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: StructArrayInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractStructArrayInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new StructArrayInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new StructArrayInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final StructArrayInterfaceServiceFactory INSTANCE = createInstance(); + } + + private StructArrayInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static StructArrayInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + StructArrayInterfaceServiceFactory t = new StructArrayInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java new file mode 100644 index 0000000..cc982df --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed1.testbed1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter; +import testbed1.testbed1_android_service.StructArrayInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed1.testbed1_impl; . +public class StructArrayInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "StructArrayInterfaceStarter"; + + + + public static IStructArrayInterface start(Context context) { + stop(context); + androidService = new Intent(context, StructArrayInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + StructArrayInterfaceServiceFactory factory = StructArrayInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for StructArrayInterfaceServiceFactory"); + return StructArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java new file mode 100644 index 0000000..1bd3a73 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java @@ -0,0 +1,492 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_android_service.IStructInterfaceServiceFactory; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class StructInterfaceServiceAdapter extends Service +{ + private static final String TAG = "StructInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IStructInterface mBackendService; + private static IStructInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public StructInterfaceServiceAdapter() + { + } + + public static IStructInterface setService(IStructInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(StructInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: StructInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(StructInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(StructInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IStructInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (StructInterfaceMessageType.fromInteger(msg.what) != StructInterfaceMessageType.REGISTER_CLIENT + && StructInterfaceMessageType.fromInteger(msg.what) != StructInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: StructInterfaceMessageType" + StructInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (StructInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool propBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt propInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); + mBackendService.setPropInt(propInt); + break; + } + case PROP_PropFloat: + { + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat propFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); + mBackendService.setPropFloat(propFloat); + break; + } + case PROP_PropString: + { + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString propString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); + mBackendService.setPropString(propString); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructBool paramBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); + + StructBool result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = StructInterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new StructBoolParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncIntReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructInt paramInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); + + StructInt result = mBackendService.funcInt(paramInt); + + Message respMsg = new Message(); + respMsg.what = StructInterfaceMessageType.RPC_FuncIntResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new StructIntParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloatReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructFloat paramFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); + + StructFloat result = mBackendService.funcFloat(paramFloat); + + Message respMsg = new Message(); + respMsg.what = StructInterfaceMessageType.RPC_FuncFloatResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new StructFloatParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncStringReq: { + + Bundle data = msg.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructString paramString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); + + StructString result = mBackendService.funcString(paramString); + + Message respMsg = new Message(); + respMsg.what = StructInterfaceMessageType.RPC_FuncStringResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new StructStringParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = StructInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + StructBool propBool = mBackendService.getPropBool(); + + data.putParcelable("propBool", new StructBoolParcelable(propBool)); + StructInt propInt = mBackendService.getPropInt(); + + data.putParcelable("propInt", new StructIntParcelable(propInt)); + StructFloat propFloat = mBackendService.getPropFloat(); + + data.putParcelable("propFloat", new StructFloatParcelable(propFloat)); + StructString propString = mBackendService.getPropString(); + + data.putParcelable("propString", new StructStringParcelable(propString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(StructBool propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propBool", new StructBoolParcelable(propBool)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(StructInt propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propInt", new StructIntParcelable(propInt)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloatChanged(StructFloat propFloat){ + Log.i(TAG, "New value for PropFloat from backend" + propFloat); + + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SET_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propFloat", new StructFloatParcelable(propFloat)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropStringChanged(StructString propString){ + Log.i(TAG, "New value for PropString from backend" + propString); + + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SET_PropString.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propString", new StructStringParcelable(propString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(StructBool paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramBool", new StructBoolParcelable(paramBool)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt(StructInt paramInt){ + Log.i(TAG, "New singal for SigInt = "+ " " + paramInt); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SIG_SigInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramInt", new StructIntParcelable(paramInt)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat(StructFloat paramFloat){ + Log.i(TAG, "New singal for SigFloat = "+ " " + paramFloat); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SIG_SigFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramFloat", new StructFloatParcelable(paramFloat)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigString(StructString paramString){ + Log.i(TAG, "New singal for SigString = "+ " " + paramString); + Message msg = new Message(); + msg.what = StructInterfaceMessageType.SIG_SigString.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramString", new StructStringParcelable(paramString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java new file mode 100644 index 0000000..302bc54 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import testbed1.testbed1_android_service.IStructInterfaceServiceFactory; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_impl.StructInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton StructInterfaceServiceFactory thread for the system. This is a thread for + * StructInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class StructInterfaceServiceFactory extends HandlerThread implements IStructInterfaceServiceFactory +{ + private StructInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static StructInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: StructInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractStructInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new StructInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new StructInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final StructInterfaceServiceFactory INSTANCE = createInstance(); + } + + private StructInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static StructInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + StructInterfaceServiceFactory t = new StructInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java new file mode 100644 index 0000000..bc0bb0e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed1.testbed1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_service.StructInterfaceServiceAdapter; +import testbed1.testbed1_android_service.StructInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed1.testbed1_impl; . +public class StructInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "StructInterfaceStarter"; + + + + public static IStructInterface start(Context context) { + stop(context); + androidService = new Intent(context, StructInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + StructInterfaceServiceFactory factory = StructInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for StructInterfaceServiceFactory"); + return StructInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..6167db6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java @@ -0,0 +1,605 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.Testbed1TestHelper; + + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_android_service.IStructArrayInterfaceServiceFactory; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IStructArrayInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class StructArrayInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private StructArrayInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IStructArrayInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IStructArrayInterface backendServiceMock = mock(IStructArrayInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IStructArrayInterfaceServiceFactory serviceFactory = mock(IStructArrayInterfaceServiceFactory.class); + private IStructArrayInterfaceMessageGetter clientMessagesStorage = mock(IStructArrayInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, StructArrayInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IStructArrayInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + StructBool init_elementpropBool = new StructBool(); + // todo fill if is struct + StructBool[] initpropBool = new StructBool[]{ init_elementpropBool } ; + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + StructInt init_elementpropInt = new StructInt(); + // todo fill if is struct + StructInt[] initpropInt = new StructInt[]{ init_elementpropInt } ; + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + StructFloat init_elementpropFloat = new StructFloat(); + // todo fill if is struct + StructFloat[] initpropFloat = new StructFloat[]{ init_elementpropFloat } ; + when(backendServiceMock.getPropFloat()).thenReturn(initpropFloat); + StructString init_elementpropString = new StructString(); + // todo fill if is struct + StructString[] initpropString = new StructString[]{ init_elementpropString } ; + when(backendServiceMock.getPropString()).thenReturn(initpropString); + + + Message registerMsg = Message.obtain(null, StructArrayInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropString(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + StructBool[] receivedpropBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); + + StructInt[] receivedpropInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); + + StructFloat[] receivedpropFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); + + StructString[] receivedpropString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + // assertEquals(receivedpropBool, initpropBool); + // assertEquals(receivedpropInt, initpropInt); + // assertEquals(receivedpropFloat, initpropFloat); + // assertEquals(receivedpropString, initpropString); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, StructArrayInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(StructArrayInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IStructArrayInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + StructBool[] testpropBool = new StructBool[1]; + testpropBool[0] = Testbed1TestHelper.makeTestStructBool(); + data.putParcelableArray("propBool", StructBoolParcelable.wrapArray(testpropBool)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool( any(StructBool[].class)); + + } + + @Test + public void whenNotifiedpropBool() + { + StructBool[] testpropBool = new StructBool[1]; + testpropBool[0] = Testbed1TestHelper.makeTestStructBool(); + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + StructBool[] receivedpropBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); + + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + StructInt[] testpropInt = new StructInt[1]; + testpropInt[0] = Testbed1TestHelper.makeTestStructInt(); + data.putParcelableArray("propInt", StructIntParcelable.wrapArray(testpropInt)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt( any(StructInt[].class)); + + } + + @Test + public void whenNotifiedpropInt() + { + StructInt[] testpropInt = new StructInt[1]; + testpropInt[0] = Testbed1TestHelper.makeTestStructInt(); + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + StructInt[] receivedpropInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); + + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.PROP_PropFloat.getValue()); + Bundle data = new Bundle(); + StructFloat[] testpropFloat = new StructFloat[1]; + testpropFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelableArray("propFloat", StructFloatParcelable.wrapArray(testpropFloat)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat( any(StructFloat[].class)); + + } + + @Test + public void whenNotifiedpropFloat() + { + StructFloat[] testpropFloat = new StructFloat[1]; + testpropFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + + testedAdapterAsEventListener.onPropFloatChanged(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SET_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + + StructFloat[] receivedpropFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); + + assertEquals(receivedpropFloat, testpropFloat); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.PROP_PropString.getValue()); + Bundle data = new Bundle(); + StructString[] testpropString = new StructString[1]; + testpropString[0] = Testbed1TestHelper.makeTestStructString(); + data.putParcelableArray("propString", StructStringParcelable.wrapArray(testpropString)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropString( any(StructString[].class)); + + } + + @Test + public void whenNotifiedpropString() + { + StructString[] testpropString = new StructString[1]; + testpropString[0] = Testbed1TestHelper.makeTestStructString(); + + testedAdapterAsEventListener.onPropStringChanged(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SET_PropString.getValue(), response.what); + Bundle data = response.getData(); + + + StructString[] receivedpropString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); + + assertEquals(receivedpropString, testpropString); + } + @Test + public void whenNotifiedsigBool() + { + StructBool[] testparamBool = new StructBool[1]; + testparamBool[0] = Testbed1TestHelper.makeTestStructBool(); + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool[] receivedparamBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); + assertEquals(receivedparamBool, testparamBool); +} + @Test + public void whenNotifiedsigInt() + { + StructInt[] testparamInt = new StructInt[1]; + testparamInt[0] = Testbed1TestHelper.makeTestStructInt(); + + testedAdapterAsEventListener.onSigInt(testparamInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SIG_SigInt.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt[] receivedparamInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); + assertEquals(receivedparamInt, testparamInt); +} + @Test + public void whenNotifiedsigFloat() + { + StructFloat[] testparamFloat = new StructFloat[1]; + testparamFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + + testedAdapterAsEventListener.onSigFloat(testparamFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SIG_SigFloat.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat[] receivedparamFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); + assertEquals(receivedparamFloat, testparamFloat); +} + @Test + public void whenNotifiedsigString() + { + StructString[] testparamString = new StructString[1]; + testparamString[0] = Testbed1TestHelper.makeTestStructString(); + + testedAdapterAsEventListener.onSigString(testparamString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SIG_SigString.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString[] receivedparamString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); + assertEquals(receivedparamString, testparamString); +} + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructBool[] testparamBool = new StructBool[1]; + testparamBool[0] = Testbed1TestHelper.makeTestStructBool(); + data.putParcelableArray("paramBool", StructBoolParcelable.wrapArray(testparamBool)); + StructBool[] returnedValue = new StructBool[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructBool(); + + + when(backendServiceMock.funcBool( any(StructBool[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool( any(StructBool[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + StructBool[] receivedByClient = StructBoolParcelable.unwrapArray((StructBoolParcelable[])resp_data.getParcelableArray("result", StructBoolParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncIntRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncIntReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructInt[] testparamInt = new StructInt[1]; + testparamInt[0] = Testbed1TestHelper.makeTestStructInt(); + data.putParcelableArray("paramInt", StructIntParcelable.wrapArray(testparamInt)); + StructInt[] returnedValue = new StructInt[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructInt(); + + + when(backendServiceMock.funcInt( any(StructInt[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt( any(StructInt[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.RPC_FuncIntResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructIntParcelable.class.getClassLoader()); + StructInt[] receivedByClient = StructIntParcelable.unwrapArray((StructIntParcelable[])resp_data.getParcelableArray("result", StructIntParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloatRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncFloatReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructFloat[] testparamFloat = new StructFloat[1]; + testparamFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelableArray("paramFloat", StructFloatParcelable.wrapArray(testparamFloat)); + StructFloat[] returnedValue = new StructFloat[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructFloat(); + + + when(backendServiceMock.funcFloat( any(StructFloat[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat( any(StructFloat[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.RPC_FuncFloatResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + StructFloat[] receivedByClient = StructFloatParcelable.unwrapArray((StructFloatParcelable[])resp_data.getParcelableArray("result", StructFloatParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncStringRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncStringReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructString[] testparamString = new StructString[1]; + testparamString[0] = Testbed1TestHelper.makeTestStructString(); + data.putParcelableArray("paramString", StructStringParcelable.wrapArray(testparamString)); + StructString[] returnedValue = new StructString[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructString(); + + + when(backendServiceMock.funcString( any(StructString[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcString( any(StructString[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.RPC_FuncStringResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructStringParcelable.class.getClassLoader()); + StructString[] receivedByClient = StructStringParcelable.unwrapArray((StructStringParcelable[])resp_data.getParcelableArray("result", StructStringParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..7c4ca08 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java @@ -0,0 +1,581 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed1.testbed1_android_service.StructInterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.Testbed1TestHelper; + + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_android_service.IStructInterfaceServiceFactory; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IStructInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class StructInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private StructInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IStructInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IStructInterface backendServiceMock = mock(IStructInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IStructInterfaceServiceFactory serviceFactory = mock(IStructInterfaceServiceFactory.class); + private IStructInterfaceMessageGetter clientMessagesStorage = mock(IStructInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, StructInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IStructInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + StructBool initpropBool = new StructBool(); + //TODO fill fields + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + StructInt initpropInt = new StructInt(); + //TODO fill fields + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + StructFloat initpropFloat = new StructFloat(); + //TODO fill fields + when(backendServiceMock.getPropFloat()).thenReturn(initpropFloat); + StructString initpropString = new StructString(); + //TODO fill fields + when(backendServiceMock.getPropString()).thenReturn(initpropString); + + + Message registerMsg = Message.obtain(null, StructInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropString(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + StructBool receivedpropBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); + + StructInt receivedpropInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); + + StructFloat receivedpropFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); + + StructString receivedpropString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + // assertEquals(receivedpropBool, initpropBool); + // assertEquals(receivedpropInt, initpropInt); + // assertEquals(receivedpropFloat, initpropFloat); + // assertEquals(receivedpropString, initpropString); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, StructInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(StructInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IStructInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + StructBool testpropBool = Testbed1TestHelper.makeTestStructBool(); + data.putParcelable("propBool", new StructBoolParcelable(testpropBool)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool( any(StructBool.class)); + + } + + @Test + public void whenNotifiedpropBool() + { + StructBool testpropBool = Testbed1TestHelper.makeTestStructBool(); + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + StructBool receivedpropBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); + + assertEquals(receivedpropBool, testpropBool); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + StructInt testpropInt = Testbed1TestHelper.makeTestStructInt(); + data.putParcelable("propInt", new StructIntParcelable(testpropInt)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt( any(StructInt.class)); + + } + + @Test + public void whenNotifiedpropInt() + { + StructInt testpropInt = Testbed1TestHelper.makeTestStructInt(); + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + StructInt receivedpropInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); + + assertEquals(receivedpropInt, testpropInt); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.PROP_PropFloat.getValue()); + Bundle data = new Bundle(); + StructFloat testpropFloat = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelable("propFloat", new StructFloatParcelable(testpropFloat)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat( any(StructFloat.class)); + + } + + @Test + public void whenNotifiedpropFloat() + { + StructFloat testpropFloat = Testbed1TestHelper.makeTestStructFloat(); + + testedAdapterAsEventListener.onPropFloatChanged(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SET_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + + StructFloat receivedpropFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); + + assertEquals(receivedpropFloat, testpropFloat); + } +//TODO do not add when a property is readonly + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.PROP_PropString.getValue()); + Bundle data = new Bundle(); + StructString testpropString = Testbed1TestHelper.makeTestStructString(); + data.putParcelable("propString", new StructStringParcelable(testpropString)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropString( any(StructString.class)); + + } + + @Test + public void whenNotifiedpropString() + { + StructString testpropString = Testbed1TestHelper.makeTestStructString(); + + testedAdapterAsEventListener.onPropStringChanged(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SET_PropString.getValue(), response.what); + Bundle data = response.getData(); + + + StructString receivedpropString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); + + assertEquals(receivedpropString, testpropString); + } + @Test + public void whenNotifiedsigBool() + { + StructBool testparamBool = Testbed1TestHelper.makeTestStructBool(); + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + StructBool receivedparamBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); + assertEquals(receivedparamBool, testparamBool); +} + @Test + public void whenNotifiedsigInt() + { + StructInt testparamInt = Testbed1TestHelper.makeTestStructInt(); + + testedAdapterAsEventListener.onSigInt(testparamInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SIG_SigInt.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + StructInt receivedparamInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); + assertEquals(receivedparamInt, testparamInt); +} + @Test + public void whenNotifiedsigFloat() + { + StructFloat testparamFloat = Testbed1TestHelper.makeTestStructFloat(); + + testedAdapterAsEventListener.onSigFloat(testparamFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SIG_SigFloat.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + StructFloat receivedparamFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); + assertEquals(receivedparamFloat, testparamFloat); +} + @Test + public void whenNotifiedsigString() + { + StructString testparamString = Testbed1TestHelper.makeTestStructString(); + + testedAdapterAsEventListener.onSigString(testparamString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.SIG_SigString.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + StructString receivedparamString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); + assertEquals(receivedparamString, testparamString); +} + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructBool testparamBool = Testbed1TestHelper.makeTestStructBool(); + data.putParcelable("paramBool", new StructBoolParcelable(testparamBool)); + StructBool returnedValue = Testbed1TestHelper.makeTestStructBool(); + + + when(backendServiceMock.funcBool( any(StructBool.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool( any(StructBool.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + StructBool receivedByClient = resp_data.getParcelable("result", StructBoolParcelable.class).getStructBool(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncIntRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncIntReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructInt testparamInt = Testbed1TestHelper.makeTestStructInt(); + data.putParcelable("paramInt", new StructIntParcelable(testparamInt)); + StructInt returnedValue = Testbed1TestHelper.makeTestStructInt(); + + + when(backendServiceMock.funcInt( any(StructInt.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt( any(StructInt.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.RPC_FuncIntResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructIntParcelable.class.getClassLoader()); + StructInt receivedByClient = resp_data.getParcelable("result", StructIntParcelable.class).getStructInt(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloatRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncFloatReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructFloat testparamFloat = Testbed1TestHelper.makeTestStructFloat(); + data.putParcelable("paramFloat", new StructFloatParcelable(testparamFloat)); + StructFloat returnedValue = Testbed1TestHelper.makeTestStructFloat(); + + + when(backendServiceMock.funcFloat( any(StructFloat.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat( any(StructFloat.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.RPC_FuncFloatResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + StructFloat receivedByClient = resp_data.getParcelable("result", StructFloatParcelable.class).getStructFloat(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncStringRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructInterfaceMessageType.RPC_FuncStringReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructString testparamString = Testbed1TestHelper.makeTestStructString(); + data.putParcelable("paramString", new StructStringParcelable(testparamString)); + StructString returnedValue = Testbed1TestHelper.makeTestStructString(); + + + when(backendServiceMock.funcString( any(StructString.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcString( any(StructString.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructInterfaceMessageType.RPC_FuncStringResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructStringParcelable.class.getClassLoader()); + StructString receivedByClient = resp_data.getParcelable("result", StructStringParcelable.class).getStructString(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed1/testbed1_api/additions.gradle b/goldenmaster/testbed1/testbed1_api/additions.gradle new file mode 100644 index 0000000..bef0500 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'testbed1.testbed1_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/testbed1/testbed1_api/build.gradle b/goldenmaster/testbed1/testbed1_api/build.gradle new file mode 100644 index 0000000..1dc20a0 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "testbed1" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java new file mode 100644 index 0000000..eddf89f --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java @@ -0,0 +1,85 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.IStructArrayInterface; +//TODO imported/extern modules +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractStructArrayInterface implements IStructArrayInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IStructArrayInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IStructArrayInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(StructBool[] newValue) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(StructInt[] newValue) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + @Override + public void firePropFloatChanged(StructFloat[] newValue) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onPropFloatChanged(newValue); + } + } + + @Override + public void firePropStringChanged(StructString[] newValue) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onPropStringChanged(newValue); + } + } + + @Override + public void fireSigBool(StructBool[] paramBool) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + @Override + public void fireSigInt(StructInt[] paramInt) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onSigInt(paramInt); + } + } + + @Override + public void fireSigFloat(StructFloat[] paramFloat) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onSigFloat(paramFloat); + } + } + + @Override + public void fireSigString(StructString[] paramString) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onSigString(paramString); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java new file mode 100644 index 0000000..f4a9721 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java @@ -0,0 +1,85 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +//TODO imported/extern modules +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractStructInterface implements IStructInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IStructInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IStructInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(StructBool newValue) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(StructInt newValue) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + @Override + public void firePropFloatChanged(StructFloat newValue) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onPropFloatChanged(newValue); + } + } + + @Override + public void firePropStringChanged(StructString newValue) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onPropStringChanged(newValue); + } + } + + @Override + public void fireSigBool(StructBool paramBool) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + @Override + public void fireSigInt(StructInt paramInt) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onSigInt(paramInt); + } + } + + @Override + public void fireSigFloat(StructFloat paramFloat) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onSigFloat(paramFloat); + } + } + + @Override + public void fireSigString(StructString paramString) { + for (IStructInterfaceEventListener listener : listeners) { + listener.onSigString(paramString); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IStructInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java new file mode 100644 index 0000000..533ed1c --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java @@ -0,0 +1,48 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + +import java.util.concurrent.CompletableFuture; + + + public interface IStructArrayInterface { + // properties + void setPropBool(StructBool[] propBool); + StructBool[] getPropBool(); + void firePropBoolChanged(StructBool[] newValue); + + void setPropInt(StructInt[] propInt); + StructInt[] getPropInt(); + void firePropIntChanged(StructInt[] newValue); + + void setPropFloat(StructFloat[] propFloat); + StructFloat[] getPropFloat(); + void firePropFloatChanged(StructFloat[] newValue); + + void setPropString(StructString[] propString); + StructString[] getPropString(); + void firePropStringChanged(StructString[] newValue); + + // methods + StructBool[] funcBool(StructBool[] paramBool); + CompletableFuture funcBoolAsync(StructBool[] paramBool); + StructInt[] funcInt(StructInt[] paramInt); + CompletableFuture funcIntAsync(StructInt[] paramInt); + StructFloat[] funcFloat(StructFloat[] paramFloat); + CompletableFuture funcFloatAsync(StructFloat[] paramFloat); + StructString[] funcString(StructString[] paramString); + CompletableFuture funcStringAsync(StructString[] paramString); + public void fireSigBool(StructBool[] paramBool); + public void fireSigInt(StructInt[] paramInt); + public void fireSigFloat(StructFloat[] paramFloat); + public void fireSigString(StructString[] paramString); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IStructArrayInterfaceEventListener listener); + void removeEventListener(IStructArrayInterfaceEventListener listener); + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java new file mode 100644 index 0000000..7e31899 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java @@ -0,0 +1,17 @@ +package testbed1.testbed1_api; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + + public interface IStructArrayInterfaceEventListener { + void onPropBoolChanged(StructBool[] newValue); + void onPropIntChanged(StructInt[] newValue); + void onPropFloatChanged(StructFloat[] newValue); + void onPropStringChanged(StructString[] newValue); + void onSigBool(StructBool[] paramBool); + void onSigInt(StructInt[] paramInt); + void onSigFloat(StructFloat[] paramFloat); + void onSigString(StructString[] paramString); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java new file mode 100644 index 0000000..a4ce431 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java @@ -0,0 +1,48 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + +import java.util.concurrent.CompletableFuture; + + + public interface IStructInterface { + // properties + void setPropBool(StructBool propBool); + StructBool getPropBool(); + void firePropBoolChanged(StructBool newValue); + + void setPropInt(StructInt propInt); + StructInt getPropInt(); + void firePropIntChanged(StructInt newValue); + + void setPropFloat(StructFloat propFloat); + StructFloat getPropFloat(); + void firePropFloatChanged(StructFloat newValue); + + void setPropString(StructString propString); + StructString getPropString(); + void firePropStringChanged(StructString newValue); + + // methods + StructBool funcBool(StructBool paramBool); + CompletableFuture funcBoolAsync(StructBool paramBool); + StructInt funcInt(StructInt paramInt); + CompletableFuture funcIntAsync(StructInt paramInt); + StructFloat funcFloat(StructFloat paramFloat); + CompletableFuture funcFloatAsync(StructFloat paramFloat); + StructString funcString(StructString paramString); + CompletableFuture funcStringAsync(StructString paramString); + public void fireSigBool(StructBool paramBool); + public void fireSigInt(StructInt paramInt); + public void fireSigFloat(StructFloat paramFloat); + public void fireSigString(StructString paramString); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IStructInterfaceEventListener listener); + void removeEventListener(IStructInterfaceEventListener listener); + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java new file mode 100644 index 0000000..66dd3ac --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java @@ -0,0 +1,17 @@ +package testbed1.testbed1_api; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + + public interface IStructInterfaceEventListener { + void onPropBoolChanged(StructBool newValue); + void onPropIntChanged(StructInt newValue); + void onPropFloatChanged(StructFloat newValue); + void onPropStringChanged(StructString newValue); + void onSigBool(StructBool paramBool); + void onSigInt(StructInt paramInt); + void onSigFloat(StructFloat paramFloat); + void onSigString(StructString paramString); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBool.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBool.java new file mode 100644 index 0000000..d05e320 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBool.java @@ -0,0 +1,43 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructBool { + + public StructBool(boolean fieldBool) + { + this.fieldBool = fieldBool; + } + + public StructBool() + { + } + @JsonProperty("field_bool") + public boolean fieldBool; + + public StructBool(StructBool other) + { + this.fieldBool = other.fieldBool; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructBool)) return false; + StructBool other = (StructBool) o; + + return + this.fieldBool == other.fieldBool; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Boolean.hashCode(fieldBool); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloat.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloat.java new file mode 100644 index 0000000..6bba29d --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloat.java @@ -0,0 +1,43 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructFloat { + + public StructFloat(float fieldFloat) + { + this.fieldFloat = fieldFloat; + } + + public StructFloat() + { + } + @JsonProperty("field_float") + public float fieldFloat; + + public StructFloat(StructFloat other) + { + this.fieldFloat = other.fieldFloat; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructFloat)) return false; + StructFloat other = (StructFloat) o; + + return + this.fieldFloat == other.fieldFloat; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Float.hashCode(fieldFloat); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructInt.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructInt.java new file mode 100644 index 0000000..e3da5d1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructInt.java @@ -0,0 +1,43 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructInt { + + public StructInt(int fieldInt) + { + this.fieldInt = fieldInt; + } + + public StructInt() + { + } + @JsonProperty("field_int") + public int fieldInt; + + public StructInt(StructInt other) + { + this.fieldInt = other.fieldInt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructInt)) return false; + StructInt other = (StructInt) o; + + return + this.fieldInt == other.fieldInt; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(fieldInt); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructString.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructString.java new file mode 100644 index 0000000..c42d905 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructString.java @@ -0,0 +1,43 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructString { + + public StructString(String fieldString) + { + this.fieldString = fieldString; + } + + public StructString() + { + } + @JsonProperty("field_string") + public String fieldString; + + public StructString(StructString other) + { + this.fieldString = other.fieldString; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructString)) return false; + StructString other = (StructString) o; + + return + this.fieldString == other.fieldString; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + (fieldString != null ? fieldString.hashCode() : 0); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java new file mode 100644 index 0000000..7d618c6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java @@ -0,0 +1,39 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class Testbed1TestHelper +{ + + static public StructBool makeTestStructBool() + { + StructBool testStruct = new StructBool(); + testStruct.fieldBool = true; + return testStruct; + } + + static public StructInt makeTestStructInt() + { + StructInt testStruct = new StructInt(); + testStruct.fieldInt = 1; + return testStruct; + } + + static public StructFloat makeTestStructFloat() + { + StructFloat testStruct = new StructFloat(); + testStruct.fieldFloat = 1.0f; + return testStruct; + } + + static public StructString makeTestStructString() + { + StructString testStruct = new StructString(); + testStruct.fieldString = new String("xyz"); + return testStruct; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/build.gradle b/goldenmaster/testbed1/testbed1_client_example/build.gradle new file mode 100644 index 0000000..b14c149 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'testbed1.testbed1_client_example' + compileSdk 35 + + defaultConfig { + applicationId "testbed1.testbed1_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':testbed1_api') + implementation project(':testbed1_android_messenger') + implementation project(':testbed1_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/AndroidManifest.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..916014a --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java new file mode 100644 index 0000000..9b76fde --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java @@ -0,0 +1,354 @@ +package testbed1.testbed1_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import testbed1.testbed1_android_client.StructInterfaceClient; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class Testbed1TestClientApp extends Activity implements IStructInterfaceEventListener +{ + + private static final String TAG = "Testbed1TestClientApp"; + + private StructInterfaceClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "testbed1.testbed1serviceexample.Testbed1TestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bPropBool = new Button(this); + bPropBool.setText("Set propBool"); + bPropBool.setBackgroundColor(Color.GREEN); + + bPropBool.setOnClickListener(v -> { + StructBool newPropBool = new StructBool(mClient.getPropBool();); + //TODO increment + Log.i(TAG, "SET propBool" + newPropBool); + mClient.setPropBool(newPropBool); + }); + propertyButtonsLine.addView(bPropBool); + Button bPropInt = new Button(this); + bPropInt.setText("Set propInt"); + bPropInt.setBackgroundColor(Color.GREEN); + + bPropInt.setOnClickListener(v -> { + StructInt newPropInt = new StructInt(mClient.getPropInt();); + //TODO increment + Log.i(TAG, "SET propInt" + newPropInt); + mClient.setPropInt(newPropInt); + }); + propertyButtonsLine.addView(bPropInt); + Button bPropFloat = new Button(this); + bPropFloat.setText("Set propFloat"); + bPropFloat.setBackgroundColor(Color.GREEN); + + bPropFloat.setOnClickListener(v -> { + StructFloat newPropFloat = new StructFloat(mClient.getPropFloat();); + //TODO increment + Log.i(TAG, "SET propFloat" + newPropFloat); + mClient.setPropFloat(newPropFloat); + }); + propertyButtonsLine.addView(bPropFloat); + Button bPropString = new Button(this); + bPropString.setText("Set propString"); + bPropString.setBackgroundColor(Color.GREEN); + + bPropString.setOnClickListener(v -> { + StructString newPropString = new StructString(mClient.getPropString();); + //TODO increment + Log.i(TAG, "SET propString" + newPropString); + mClient.setPropString(newPropString); + }); + propertyButtonsLine.addView(bPropString); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bFuncBool = new Button(this); + bFuncBool.setText("funcBool"); + + bFuncBool.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD funcBool "); + StructBool paramBool = new StructBool(); + CompletableFuture method_res + = mClient.funcBoolAsync(paramBool).thenApply( + i -> { + outputTextVieMethodRes.setText("Got funcBool result "+ i); + return i; + }); + }); + bFuncBool.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFuncBool); + Button bFuncInt = new Button(this); + bFuncInt.setText("funcInt"); + + bFuncInt.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD funcInt "); + StructInt paramInt = new StructInt(); + CompletableFuture method_res + = mClient.funcIntAsync(paramInt).thenApply( + i -> { + outputTextVieMethodRes.setText("Got funcInt result "+ i); + return i; + }); + }); + bFuncInt.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFuncInt); + Button bFuncFloat = new Button(this); + bFuncFloat.setText("funcFloat"); + + bFuncFloat.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD funcFloat "); + StructFloat paramFloat = new StructFloat(); + CompletableFuture method_res + = mClient.funcFloatAsync(paramFloat).thenApply( + i -> { + outputTextVieMethodRes.setText("Got funcFloat result "+ i); + return i; + }); + }); + bFuncFloat.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFuncFloat); + Button bFuncString = new Button(this); + bFuncString.setText("funcString"); + + bFuncString.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD funcString "); + StructString paramString = new StructString(); + CompletableFuture method_res + = mClient.funcStringAsync(paramString).thenApply( + i -> { + outputTextVieMethodRes.setText("Got funcString result "+ i); + return i; + }); + }); + bFuncString.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFuncString); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new StructInterfaceClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onPropBoolChanged(StructBool newValue) + { + outputTextViewProp.setText("Property from service: propBool " + newValue); + Log.w(TAG, "Property from service: propBool " + newValue); + } + @Override + public void onPropIntChanged(StructInt newValue) + { + outputTextViewProp.setText("Property from service: propInt " + newValue); + Log.w(TAG, "Property from service: propInt " + newValue); + } + @Override + public void onPropFloatChanged(StructFloat newValue) + { + outputTextViewProp.setText("Property from service: propFloat " + newValue); + Log.w(TAG, "Property from service: propFloat " + newValue); + } + @Override + public void onPropStringChanged(StructString newValue) + { + outputTextViewProp.setText("Property from service: propString " + newValue); + Log.w(TAG, "Property from service: propString " + newValue); + } + @Override + public void onSigBool(StructBool paramBool) + { + String text = "Signal sigBool "+ " " + paramBool; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSigInt(StructInt paramInt) + { + String text = "Signal sigInt "+ " " + paramInt; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSigFloat(StructFloat paramFloat) + { + String text = "Signal sigFloat "+ " " + paramFloat; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSigString(StructString paramString) + { + String text = "Signal sigString "+ " " + paramString; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/values-night/themes.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/colors.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/strings.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..168754b --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Testbed1TestClientApp + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/themes.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_impl/additions.gradle b/goldenmaster/testbed1/testbed1_impl/additions.gradle new file mode 100644 index 0000000..bec9b44 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'testbed1.testbed1_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_impl/build.gradle b/goldenmaster/testbed1/testbed1_impl/build.gradle new file mode 100644 index 0000000..6ec44c8 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed1" +version = "1.0.0" + +android { + namespace 'testbed1.testbed1_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed1_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java new file mode 100644 index 0000000..f3e2da5 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java @@ -0,0 +1,221 @@ +package testbed1.testbed1_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class StructArrayInterfaceService extends AbstractStructArrayInterface { + + private final static String TAG = "StructArrayInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private StructBool[] m_propBool = new StructBool[]{}; + private StructInt[] m_propInt = new StructInt[]{}; + private StructFloat[] m_propFloat = new StructFloat[]{}; + private StructString[] m_propString = new StructString[]{}; + + public StructArrayInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(StructBool[] propBool) + { + Log.i(TAG, "request setPropBool called "); + if (! Arrays.equals(m_propBool, propBool)) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public StructBool[] getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(StructInt[] propInt) + { + Log.i(TAG, "request setPropInt called "); + if (! Arrays.equals(m_propInt, propInt)) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public StructInt[] getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + @Override + public void setPropFloat(StructFloat[] propFloat) + { + Log.i(TAG, "request setPropFloat called "); + if (! Arrays.equals(m_propFloat, propFloat)) + { + m_propFloat = propFloat; + onPropFloatChanged(m_propFloat); + } + + } + + @Override + public StructFloat[] getPropFloat() + { + Log.i(TAG, "request getPropFloat called,"); + return m_propFloat; + } + + + @Override + public void setPropString(StructString[] propString) + { + Log.i(TAG, "request setPropString called "); + if (! Arrays.equals(m_propString, propString)) + { + m_propString = propString; + onPropStringChanged(m_propString); + } + + } + + @Override + public StructString[] getPropString() + { + Log.i(TAG, "request getPropString called,"); + return m_propString; + } + + + // methods + + @Override + public StructBool[] funcBool(StructBool[] paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return new StructBool[]{}; + } + + @Override + public CompletableFuture funcBoolAsync(StructBool[] paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public StructInt[] funcInt(StructInt[] paramInt) { + Log.w(TAG, "request method funcInt called, returnig default"); + return new StructInt[]{}; + } + + @Override + public CompletableFuture funcIntAsync(StructInt[] paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public StructFloat[] funcFloat(StructFloat[] paramFloat) { + Log.w(TAG, "request method funcFloat called, returnig default"); + return new StructFloat[]{}; + } + + @Override + public CompletableFuture funcFloatAsync(StructFloat[] paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public StructString[] funcString(StructString[] paramString) { + Log.w(TAG, "request method funcString called, returnig default"); + return new StructString[]{}; + } + + @Override + public CompletableFuture funcStringAsync(StructString[] paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(StructBool[] newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(StructInt[] newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + private void onPropFloatChanged(StructFloat[] newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + private void onPropStringChanged(StructString[] newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onSigBool(StructBool[] paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(StructInt[] paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloat[] paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigString(StructString[] paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java new file mode 100644 index 0000000..81962e6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java @@ -0,0 +1,221 @@ +package testbed1.testbed1_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class StructInterfaceService extends AbstractStructInterface { + + private final static String TAG = "StructInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private StructBool m_propBool = new StructBool(); + private StructInt m_propInt = new StructInt(); + private StructFloat m_propFloat = new StructFloat(); + private StructString m_propString = new StructString(); + + public StructInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(StructBool propBool) + { + Log.i(TAG, "request setPropBool called "); + if (! m_propBool.equals(propBool)) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public StructBool getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(StructInt propInt) + { + Log.i(TAG, "request setPropInt called "); + if (! m_propInt.equals(propInt)) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public StructInt getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + @Override + public void setPropFloat(StructFloat propFloat) + { + Log.i(TAG, "request setPropFloat called "); + if (! m_propFloat.equals(propFloat)) + { + m_propFloat = propFloat; + onPropFloatChanged(m_propFloat); + } + + } + + @Override + public StructFloat getPropFloat() + { + Log.i(TAG, "request getPropFloat called,"); + return m_propFloat; + } + + + @Override + public void setPropString(StructString propString) + { + Log.i(TAG, "request setPropString called "); + if (! m_propString.equals(propString)) + { + m_propString = propString; + onPropStringChanged(m_propString); + } + + } + + @Override + public StructString getPropString() + { + Log.i(TAG, "request getPropString called,"); + return m_propString; + } + + + // methods + + @Override + public StructBool funcBool(StructBool paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return new StructBool(); + } + + @Override + public CompletableFuture funcBoolAsync(StructBool paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public StructInt funcInt(StructInt paramInt) { + Log.w(TAG, "request method funcInt called, returnig default"); + return new StructInt(); + } + + @Override + public CompletableFuture funcIntAsync(StructInt paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public StructFloat funcFloat(StructFloat paramFloat) { + Log.w(TAG, "request method funcFloat called, returnig default"); + return new StructFloat(); + } + + @Override + public CompletableFuture funcFloatAsync(StructFloat paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public StructString funcString(StructString paramString) { + Log.w(TAG, "request method funcString called, returnig default"); + return new StructString(); + } + + @Override + public CompletableFuture funcStringAsync(StructString paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(StructBool newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(StructInt newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + private void onPropFloatChanged(StructFloat newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + private void onPropStringChanged(StructString newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onSigBool(StructBool paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(StructInt paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloat paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigString(StructString paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java new file mode 100644 index 0000000..66a12a6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java @@ -0,0 +1,256 @@ +package testbed1.testbed1jniclient; + +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; + +import testbed1.testbed1_android_client.StructArrayInterfaceClient; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class StructArrayInterfaceJniClient extends AbstractStructArrayInterface implements IStructArrayInterfaceEventListener +{ + + private static final String TAG = "StructArrayInterfaceJniClient"; + + private StructArrayInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed1.testbed1jniservice.StructArrayInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(StructBool[] propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public StructBool[] getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(StructInt[] propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public StructInt[] getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + @Override + public void setPropFloat(StructFloat[] propFloat) + { + Log.i(TAG, "got request from ue, setPropFloat" + (propFloat)); + mMessengerClient.setPropFloat(propFloat); + } + @Override + public StructFloat[] getPropFloat() + { + Log.i(TAG, "got request from ue, getPropFloat"); + return mMessengerClient.getPropFloat(); + } + + @Override + public void setPropString(StructString[] propString) + { + Log.i(TAG, "got request from ue, setPropString" + (propString)); + mMessengerClient.setPropString(propString); + } + @Override + public StructString[] getPropString() + { + Log.i(TAG, "got request from ue, getPropString"); + return mMessengerClient.getPropString(); + } + + public StructBool[] funcBool(StructBool[] paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, StructBool[] paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, StructBool[] paramBool) + public CompletableFuture funcBoolAsync(StructBool[] paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + public StructInt[] funcInt(StructInt[] paramInt) + { + Log.v(TAG, "Blocking callfuncInt - should not be used "); + return mMessengerClient.funcInt(paramInt); + } + + public void funcIntAsync(String callId, StructInt[] paramInt){ + Log.v(TAG, "non blocking call funcInt "); + mMessengerClient.funcIntAsync(paramInt).thenAccept(i -> { + nativeOnFuncIntResult(i, callId);}); + } + + //Should not be called directly, use funcIntAsync(String callId, StructInt[] paramInt) + public CompletableFuture funcIntAsync(StructInt[] paramInt) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcIntAsync(paramInt); + } + public StructFloat[] funcFloat(StructFloat[] paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat - should not be used "); + return mMessengerClient.funcFloat(paramFloat); + } + + public void funcFloatAsync(String callId, StructFloat[] paramFloat){ + Log.v(TAG, "non blocking call funcFloat "); + mMessengerClient.funcFloatAsync(paramFloat).thenAccept(i -> { + nativeOnFuncFloatResult(i, callId);}); + } + + //Should not be called directly, use funcFloatAsync(String callId, StructFloat[] paramFloat) + public CompletableFuture funcFloatAsync(StructFloat[] paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloatAsync(paramFloat); + } + public StructString[] funcString(StructString[] paramString) + { + Log.v(TAG, "Blocking callfuncString - should not be used "); + return mMessengerClient.funcString(paramString); + } + + public void funcStringAsync(String callId, StructString[] paramString){ + Log.v(TAG, "non blocking call funcString "); + mMessengerClient.funcStringAsync(paramString).thenAccept(i -> { + nativeOnFuncStringResult(i, callId);}); + } + + //Should not be called directly, use funcStringAsync(String callId, StructString[] paramString) + public CompletableFuture funcStringAsync(StructString[] paramString) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcStringAsync(paramString); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new StructArrayInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(StructBool[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(StructInt[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropFloatChanged(StructFloat[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropStringChanged(StructString[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onSigBool(StructBool[] paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(StructInt[] paramInt) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigFloat(StructFloat[] paramFloat) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigString(StructString[] paramString) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + nativeOnSigString(paramString); + } + private native void nativeOnPropBoolChanged(StructBool[] propBool); + private native void nativeOnPropIntChanged(StructInt[] propInt); + private native void nativeOnPropFloatChanged(StructFloat[] propFloat); + private native void nativeOnPropStringChanged(StructString[] propString); + private native void nativeOnSigBool(StructBool[] paramBool); + private native void nativeOnSigInt(StructInt[] paramInt); + private native void nativeOnSigFloat(StructFloat[] paramFloat); + private native void nativeOnSigString(StructString[] paramString); + private native void nativeOnFuncBoolResult(StructBool[] result, String callId); + private native void nativeOnFuncIntResult(StructInt[] result, String callId); + private native void nativeOnFuncFloatResult(StructFloat[] result, String callId); + private native void nativeOnFuncStringResult(StructString[] result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java new file mode 100644 index 0000000..16e63b6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java @@ -0,0 +1,256 @@ +package testbed1.testbed1jniclient; + +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_api.IStructInterfaceEventListener; + +import testbed1.testbed1_android_client.StructInterfaceClient; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class StructInterfaceJniClient extends AbstractStructInterface implements IStructInterfaceEventListener +{ + + private static final String TAG = "StructInterfaceJniClient"; + + private StructInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed1.testbed1jniservice.StructInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(StructBool propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public StructBool getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(StructInt propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public StructInt getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + @Override + public void setPropFloat(StructFloat propFloat) + { + Log.i(TAG, "got request from ue, setPropFloat" + (propFloat)); + mMessengerClient.setPropFloat(propFloat); + } + @Override + public StructFloat getPropFloat() + { + Log.i(TAG, "got request from ue, getPropFloat"); + return mMessengerClient.getPropFloat(); + } + + @Override + public void setPropString(StructString propString) + { + Log.i(TAG, "got request from ue, setPropString" + (propString)); + mMessengerClient.setPropString(propString); + } + @Override + public StructString getPropString() + { + Log.i(TAG, "got request from ue, getPropString"); + return mMessengerClient.getPropString(); + } + + public StructBool funcBool(StructBool paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, StructBool paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, StructBool paramBool) + public CompletableFuture funcBoolAsync(StructBool paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + public StructInt funcInt(StructInt paramInt) + { + Log.v(TAG, "Blocking callfuncInt - should not be used "); + return mMessengerClient.funcInt(paramInt); + } + + public void funcIntAsync(String callId, StructInt paramInt){ + Log.v(TAG, "non blocking call funcInt "); + mMessengerClient.funcIntAsync(paramInt).thenAccept(i -> { + nativeOnFuncIntResult(i, callId);}); + } + + //Should not be called directly, use funcIntAsync(String callId, StructInt paramInt) + public CompletableFuture funcIntAsync(StructInt paramInt) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcIntAsync(paramInt); + } + public StructFloat funcFloat(StructFloat paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat - should not be used "); + return mMessengerClient.funcFloat(paramFloat); + } + + public void funcFloatAsync(String callId, StructFloat paramFloat){ + Log.v(TAG, "non blocking call funcFloat "); + mMessengerClient.funcFloatAsync(paramFloat).thenAccept(i -> { + nativeOnFuncFloatResult(i, callId);}); + } + + //Should not be called directly, use funcFloatAsync(String callId, StructFloat paramFloat) + public CompletableFuture funcFloatAsync(StructFloat paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloatAsync(paramFloat); + } + public StructString funcString(StructString paramString) + { + Log.v(TAG, "Blocking callfuncString - should not be used "); + return mMessengerClient.funcString(paramString); + } + + public void funcStringAsync(String callId, StructString paramString){ + Log.v(TAG, "non blocking call funcString "); + mMessengerClient.funcStringAsync(paramString).thenAccept(i -> { + nativeOnFuncStringResult(i, callId);}); + } + + //Should not be called directly, use funcStringAsync(String callId, StructString paramString) + public CompletableFuture funcStringAsync(StructString paramString) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcStringAsync(paramString); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new StructInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(StructBool newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(StructInt newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropFloatChanged(StructFloat newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropStringChanged(StructString newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onSigBool(StructBool paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(StructInt paramInt) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigFloat(StructFloat paramFloat) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigString(StructString paramString) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + nativeOnSigString(paramString); + } + private native void nativeOnPropBoolChanged(StructBool propBool); + private native void nativeOnPropIntChanged(StructInt propInt); + private native void nativeOnPropFloatChanged(StructFloat propFloat); + private native void nativeOnPropStringChanged(StructString propString); + private native void nativeOnSigBool(StructBool paramBool); + private native void nativeOnSigInt(StructInt paramInt); + private native void nativeOnSigFloat(StructFloat paramFloat); + private native void nativeOnSigString(StructString paramString); + private native void nativeOnFuncBoolResult(StructBool result, String callId); + private native void nativeOnFuncIntResult(StructInt result, String callId); + private native void nativeOnFuncFloatResult(StructFloat result, String callId); + private native void nativeOnFuncStringResult(StructString result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java new file mode 100644 index 0000000..fb26fa6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java @@ -0,0 +1,221 @@ +package testbed1.testbed1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class StructArrayInterfaceJniService extends AbstractStructArrayInterface { + + + private final static String TAG = "StructArrayInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public StructArrayInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(StructBool[] propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public StructBool[] getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(StructInt[] propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public StructInt[] getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + @Override + public void setPropFloat(StructFloat[] propFloat) + { + Log.i(TAG, "request setPropFloat called, will call native "); + nativeSetPropFloat(propFloat); + } + + @Override + public StructFloat[] getPropFloat() + { + Log.i(TAG, "request getPropFloat called, will call native "); + return nativeGetPropFloat(); + } + + + @Override + public void setPropString(StructString[] propString) + { + Log.i(TAG, "request setPropString called, will call native "); + nativeSetPropString(propString); + } + + @Override + public StructString[] getPropString() + { + Log.i(TAG, "request getPropString called, will call native "); + return nativeGetPropString(); + } + + + // methods + + @Override + public StructBool[] funcBool(StructBool[] paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(StructBool[] paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public StructInt[] funcInt(StructInt[] paramInt) { + Log.w(TAG, "request method funcInt called, will call native"); + return nativeFuncInt(paramInt); + } + + @Override + public CompletableFuture funcIntAsync(StructInt[] paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public StructFloat[] funcFloat(StructFloat[] paramFloat) { + Log.w(TAG, "request method funcFloat called, will call native"); + return nativeFuncFloat(paramFloat); + } + + @Override + public CompletableFuture funcFloatAsync(StructFloat[] paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public StructString[] funcString(StructString[] paramString) { + Log.w(TAG, "request method funcString called, will call native"); + return nativeFuncString(paramString); + } + + @Override + public CompletableFuture funcStringAsync(StructString[] paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(StructBool[] propBool); + private native StructBool[] nativeGetPropBool(); + + private native void nativeSetPropInt(StructInt[] propInt); + private native StructInt[] nativeGetPropInt(); + + private native void nativeSetPropFloat(StructFloat[] propFloat); + private native StructFloat[] nativeGetPropFloat(); + + private native void nativeSetPropString(StructString[] propString); + private native StructString[] nativeGetPropString(); + + // methods + private native StructBool[] nativeFuncBool(StructBool[] paramBool); + private native StructInt[] nativeFuncInt(StructInt[] paramInt); + private native StructFloat[] nativeFuncFloat(StructFloat[] paramFloat); + private native StructString[] nativeFuncString(StructString[] paramString); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(StructBool[] newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(StructInt[] newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onPropFloatChanged(StructFloat[] newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + public void onPropStringChanged(StructString[] newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onSigBool(StructBool[] paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(StructInt[] paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloat[] paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigString(StructString[] paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java new file mode 100644 index 0000000..eb901f3 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1jniservice; + +import testbed1.testbed1_android_service.IStructArrayInterfaceServiceFactory; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_api.AbstractStructArrayInterface; +import testbed1.testbed1jniservice.StructArrayInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton StructArrayInterfaceJniServiceFactory thread for the system. This is a thread for + * StructArrayInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class StructArrayInterfaceJniServiceFactory extends HandlerThread implements IStructArrayInterfaceServiceFactory +{ + private StructArrayInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static StructArrayInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: StructArrayInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractStructArrayInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new StructArrayInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new StructArrayInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final StructArrayInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private StructArrayInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static StructArrayInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + StructArrayInterfaceJniServiceFactory t = new StructArrayInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java new file mode 100644 index 0000000..2f1710c --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed1.testbed1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter; +import testbed1.testbed1jniservice.StructArrayInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class StructArrayInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "StructArrayInterfaceJniStarter"; + + + + public static IStructArrayInterface start(Context context) { + stop(context); + androidService = new Intent(context, StructArrayInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + StructArrayInterfaceJniServiceFactory factory = StructArrayInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for StructArrayInterfaceJniServiceFactory"); + return StructArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java new file mode 100644 index 0000000..239dee2 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java @@ -0,0 +1,221 @@ +package testbed1.testbed1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class StructInterfaceJniService extends AbstractStructInterface { + + + private final static String TAG = "StructInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public StructInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(StructBool propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public StructBool getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(StructInt propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public StructInt getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + @Override + public void setPropFloat(StructFloat propFloat) + { + Log.i(TAG, "request setPropFloat called, will call native "); + nativeSetPropFloat(propFloat); + } + + @Override + public StructFloat getPropFloat() + { + Log.i(TAG, "request getPropFloat called, will call native "); + return nativeGetPropFloat(); + } + + + @Override + public void setPropString(StructString propString) + { + Log.i(TAG, "request setPropString called, will call native "); + nativeSetPropString(propString); + } + + @Override + public StructString getPropString() + { + Log.i(TAG, "request getPropString called, will call native "); + return nativeGetPropString(); + } + + + // methods + + @Override + public StructBool funcBool(StructBool paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(StructBool paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public StructInt funcInt(StructInt paramInt) { + Log.w(TAG, "request method funcInt called, will call native"); + return nativeFuncInt(paramInt); + } + + @Override + public CompletableFuture funcIntAsync(StructInt paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public StructFloat funcFloat(StructFloat paramFloat) { + Log.w(TAG, "request method funcFloat called, will call native"); + return nativeFuncFloat(paramFloat); + } + + @Override + public CompletableFuture funcFloatAsync(StructFloat paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public StructString funcString(StructString paramString) { + Log.w(TAG, "request method funcString called, will call native"); + return nativeFuncString(paramString); + } + + @Override + public CompletableFuture funcStringAsync(StructString paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(StructBool propBool); + private native StructBool nativeGetPropBool(); + + private native void nativeSetPropInt(StructInt propInt); + private native StructInt nativeGetPropInt(); + + private native void nativeSetPropFloat(StructFloat propFloat); + private native StructFloat nativeGetPropFloat(); + + private native void nativeSetPropString(StructString propString); + private native StructString nativeGetPropString(); + + // methods + private native StructBool nativeFuncBool(StructBool paramBool); + private native StructInt nativeFuncInt(StructInt paramInt); + private native StructFloat nativeFuncFloat(StructFloat paramFloat); + private native StructString nativeFuncString(StructString paramString); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(StructBool newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(StructInt newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onPropFloatChanged(StructFloat newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + public void onPropStringChanged(StructString newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onSigBool(StructBool paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(StructInt paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloat paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigString(StructString paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java new file mode 100644 index 0000000..ccbb34f --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1jniservice; + +import testbed1.testbed1_android_service.IStructInterfaceServiceFactory; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_api.AbstractStructInterface; +import testbed1.testbed1jniservice.StructInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton StructInterfaceJniServiceFactory thread for the system. This is a thread for + * StructInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class StructInterfaceJniServiceFactory extends HandlerThread implements IStructInterfaceServiceFactory +{ + private StructInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static StructInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: StructInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractStructInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new StructInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new StructInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final StructInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private StructInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static StructInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + StructInterfaceJniServiceFactory t = new StructInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java new file mode 100644 index 0000000..44f63a6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed1.testbed1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_service.StructInterfaceServiceAdapter; +import testbed1.testbed1jniservice.StructInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class StructInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "StructInterfaceJniStarter"; + + + + public static IStructInterface start(Context context) { + stop(context); + androidService = new Intent(context, StructInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + StructInterfaceJniServiceFactory factory = StructInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for StructInterfaceJniServiceFactory"); + return StructInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/build.gradle b/goldenmaster/testbed1/testbed1serviceexample/build.gradle new file mode 100644 index 0000000..d2c11e1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'testbed1.testbed1serviceexample' + compileSdk 35 + + defaultConfig { + applicationId "testbed1.testbed1serviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':testbed1_api') + implementation project(':testbed1_android_messenger') + implementation project(':testbed1_android_service') + implementation project(':testbed1_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6e3e4ee --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java new file mode 100644 index 0000000..b86a772 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java @@ -0,0 +1,312 @@ +package testbed1.testbed1serviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import testbed1.testbed1_android_service.StructInterfaceServiceAdapter; +import testbed1.testbed1_android_service.StructInterfaceServiceFactory; +import testbed1.testbed1_android_service.StructInterfaceServiceStarter; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_impl.StructInterfaceService; +import java.util.concurrent.CompletableFuture; + + + +public class Testbed1TestServiceApp extends Activity implements IStructInterfaceEventListener +{ + + private static final String TAG = "Testbed1TestServiceApp"; + static Intent stub_service = null; + + + private IStructInterface mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bPropBool = new Button(this); + bPropBool.setText("Set propBool"); + bPropBool.setBackgroundColor(Color.GREEN); + + bPropBool.setOnClickListener(v -> { + StructBool newPropBool = mBackend.getPropBool(); + //TODO increment + Log.i(TAG, "SET propBool" + newPropBool); + mBackend.setPropBool(newPropBool); + }); + propertyButtonsLine.addView(bPropBool); + Button bPropInt = new Button(this); + bPropInt.setText("Set propInt"); + bPropInt.setBackgroundColor(Color.GREEN); + + bPropInt.setOnClickListener(v -> { + StructInt newPropInt = mBackend.getPropInt(); + //TODO increment + Log.i(TAG, "SET propInt" + newPropInt); + mBackend.setPropInt(newPropInt); + }); + propertyButtonsLine.addView(bPropInt); + Button bPropFloat = new Button(this); + bPropFloat.setText("Set propFloat"); + bPropFloat.setBackgroundColor(Color.GREEN); + + bPropFloat.setOnClickListener(v -> { + StructFloat newPropFloat = mBackend.getPropFloat(); + //TODO increment + Log.i(TAG, "SET propFloat" + newPropFloat); + mBackend.setPropFloat(newPropFloat); + }); + propertyButtonsLine.addView(bPropFloat); + Button bPropString = new Button(this); + bPropString.setText("Set propString"); + bPropString.setBackgroundColor(Color.GREEN); + + bPropString.setOnClickListener(v -> { + StructString newPropString = mBackend.getPropString(); + //TODO increment + Log.i(TAG, "SET propString" + newPropString); + mBackend.setPropString(newPropString); + }); + propertyButtonsLine.addView(bPropString); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSigBool = new Button(this); + bSigBool.setText("sigBool"); + + bSigBool.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sigBool "); + StructBool paramBool = new StructBool(); + mBackend.fireSigBool(paramBool); + }); + bSigBool.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSigBool); + Button bSigInt = new Button(this); + bSigInt.setText("sigInt"); + + bSigInt.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sigInt "); + StructInt paramInt = new StructInt(); + mBackend.fireSigInt(paramInt); + }); + bSigInt.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSigInt); + Button bSigFloat = new Button(this); + bSigFloat.setText("sigFloat"); + + bSigFloat.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sigFloat "); + StructFloat paramFloat = new StructFloat(); + mBackend.fireSigFloat(paramFloat); + }); + bSigFloat.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSigFloat); + Button bSigString = new Button(this); + bSigString.setText("sigString"); + + bSigString.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sigString "); + StructString paramString = new StructString(); + mBackend.fireSigString(paramString); + }); + bSigString.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSigString); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, StructInterfaceServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = StructInterfaceServiceAdapter.setService(StructInterfaceServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onPropBoolChanged(StructBool newValue) + { + outputTextViewProp.setText("Property from service: propBool " + newValue); + Log.w(TAG, "Property from service: propBool " + newValue); + } + @Override + public void onPropIntChanged(StructInt newValue) + { + outputTextViewProp.setText("Property from service: propInt " + newValue); + Log.w(TAG, "Property from service: propInt " + newValue); + } + @Override + public void onPropFloatChanged(StructFloat newValue) + { + outputTextViewProp.setText("Property from service: propFloat " + newValue); + Log.w(TAG, "Property from service: propFloat " + newValue); + } + @Override + public void onPropStringChanged(StructString newValue) + { + outputTextViewProp.setText("Property from service: propString " + newValue); + Log.w(TAG, "Property from service: propString " + newValue); + } + @Override + public void onSigBool(StructBool paramBool) + { + String text = "Signal sigBool "+ " " + paramBool; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSigInt(StructInt paramInt) + { + String text = "Signal sigInt "+ " " + paramInt; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSigFloat(StructFloat paramFloat) + { + String text = "Signal sigFloat "+ " " + paramFloat; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSigString(StructString paramString) + { + String text = "Signal sigString "+ " " + paramString; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values-night/themes.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/colors.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/strings.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..2433a62 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Testbed1TestServiceApp + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/themes.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2.api/Testbed2.java b/goldenmaster/testbed2.api/Testbed2.java deleted file mode 100644 index 93425d0..0000000 --- a/goldenmaster/testbed2.api/Testbed2.java +++ /dev/null @@ -1,421 +0,0 @@ -package testbed2.api; - -import com.fasterxml.jackson.annotation.JsonProperty; - - -public class Testbed2 { - - // enumerations - public enum Enum1 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - @JsonProperty("3") - Value3, - @JsonProperty("4") - Value4, - } - public enum Enum2 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - @JsonProperty("3") - Value3, - @JsonProperty("4") - Value4, - } - public enum Enum3 { - @JsonProperty("1") - Value1, - @JsonProperty("2") - Value2, - @JsonProperty("3") - Value3, - @JsonProperty("4") - Value4, - } - - // data structures - public static class Struct1 { - public Struct1(int field1) { - this.field1 = field1; - } - @JsonProperty("field1") - public int field1; - } - public static class Struct2 { - public Struct2(int field1, int field2) { - this.field1 = field1; - this.field2 = field2; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - } - public static class Struct3 { - public Struct3(int field1, int field2, int field3) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - @JsonProperty("field3") - public int field3; - } - public static class Struct4 { - public Struct4(int field1, int field2, int field3, int field4) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - this.field4 = field4; - } - @JsonProperty("field1") - public int field1; - @JsonProperty("field2") - public int field2; - @JsonProperty("field3") - public int field3; - @JsonProperty("field4") - public int field4; - } - public static class NestedStruct1 { - public NestedStruct1(Struct1 field1) { - this.field1 = field1; - } - @JsonProperty("field1") - public Struct1 field1; - } - public static class NestedStruct2 { - public NestedStruct2(Struct1 field1, Struct2 field2) { - this.field1 = field1; - this.field2 = field2; - } - @JsonProperty("field1") - public Struct1 field1; - @JsonProperty("field2") - public Struct2 field2; - } - public static class NestedStruct3 { - public NestedStruct3(Struct1 field1, Struct2 field2, Struct3 field3) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - } - @JsonProperty("field1") - public Struct1 field1; - @JsonProperty("field2") - public Struct2 field2; - @JsonProperty("field3") - public Struct3 field3; - } - - // interfaces - public static interface IManyParamInterfaceEventListener { - void onProp1Changed(int oldValue, int newValue); - void onProp2Changed(int oldValue, int newValue); - void onProp3Changed(int oldValue, int newValue); - void onProp4Changed(int oldValue, int newValue); - void onSig1(int param1); - void onSig2(int param1, int param2); - void onSig3(int param1, int param2, int param3); - void onSig4(int param1, int param2, int param3, int param4); - } - - public static interface IManyParamInterface { - // properties - void setProp1(int prop1); - int getProp1(); - void fireProp1Changed(int oldValue, int newValue); - - void setProp2(int prop2); - int getProp2(); - void fireProp2Changed(int oldValue, int newValue); - - void setProp3(int prop3); - int getProp3(); - void fireProp3Changed(int oldValue, int newValue); - - void setProp4(int prop4); - int getProp4(); - void fireProp4Changed(int oldValue, int newValue); - - // methods - int func1(int param1); - int func2(int param1, int param2); - int func3(int param1, int param2, int param3); - int func4(int param1, int param2, int param3, int param4); - - // signal listeners - void addEventListener(IManyParamInterfaceEventListener listener); - void removeEventListener(IManyParamInterfaceEventListener listener); - } - - public static class AbstractManyParamInterface implements IManyParamInterface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireProp3Changed(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp3Changed(oldValue, newValue); - } - } - - @Override - public void fireProp4Changed(int oldValue, int newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp4Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(int param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(int param1, int param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - @Override - public void fireSig3(int param1, int param2, int param3) { - for (ISig3EventListener listener : events) { - listener.onSig3(param1, param2, param3); - } - } - - @Override - public void fireSig4(int param1, int param2, int param3, int param4) { - for (ISig4EventListener listener : events) { - listener.onSig4(param1, param2, param3, param4); - } - } - - - } - public static interface INestedStruct1InterfaceEventListener { - void onProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue); - void onSig1(NestedStruct1 param1); - } - - public static interface INestedStruct1Interface { - // properties - void setProp1(NestedStruct1 prop1); - NestedStruct1 getProp1(); - void fireProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue); - - // methods - NestedStruct1 func1(NestedStruct1 param1); - - // signal listeners - void addEventListener(INestedStruct1InterfaceEventListener listener); - void removeEventListener(INestedStruct1InterfaceEventListener listener); - } - - public static class AbstractNestedStruct1Interface implements INestedStruct1Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(NestedStruct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - - } - public static interface INestedStruct2InterfaceEventListener { - void onProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue); - void onProp2Changed(NestedStruct2 oldValue, NestedStruct2 newValue); - void onSig1(NestedStruct1 param1); - void onSig2(NestedStruct1 param1, NestedStruct2 param2); - } - - public static interface INestedStruct2Interface { - // properties - void setProp1(NestedStruct1 prop1); - NestedStruct1 getProp1(); - void fireProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue); - - void setProp2(NestedStruct2 prop2); - NestedStruct2 getProp2(); - void fireProp2Changed(NestedStruct2 oldValue, NestedStruct2 newValue); - - // methods - NestedStruct1 func1(NestedStruct1 param1); - NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2); - - // signal listeners - void addEventListener(INestedStruct2InterfaceEventListener listener); - void removeEventListener(INestedStruct2InterfaceEventListener listener); - } - - public static class AbstractNestedStruct2Interface implements INestedStruct2Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(NestedStruct2 oldValue, NestedStruct2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(NestedStruct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(NestedStruct1 param1, NestedStruct2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - - } - public static interface INestedStruct3InterfaceEventListener { - void onProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue); - void onProp2Changed(NestedStruct2 oldValue, NestedStruct2 newValue); - void onProp3Changed(NestedStruct3 oldValue, NestedStruct3 newValue); - void onSig1(NestedStruct1 param1); - void onSig2(NestedStruct1 param1, NestedStruct2 param2); - void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); - } - - public static interface INestedStruct3Interface { - // properties - void setProp1(NestedStruct1 prop1); - NestedStruct1 getProp1(); - void fireProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue); - - void setProp2(NestedStruct2 prop2); - NestedStruct2 getProp2(); - void fireProp2Changed(NestedStruct2 oldValue, NestedStruct2 newValue); - - void setProp3(NestedStruct3 prop3); - NestedStruct3 getProp3(); - void fireProp3Changed(NestedStruct3 oldValue, NestedStruct3 newValue); - - // methods - NestedStruct1 func1(NestedStruct1 param1); - NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2); - NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); - - // signal listeners - void addEventListener(INestedStruct3InterfaceEventListener listener); - void removeEventListener(INestedStruct3InterfaceEventListener listener); - } - - public static class AbstractNestedStruct3Interface implements INestedStruct3Interface { - public Collection events = new HashSet<>(); - - public void addEventListener(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - @Override - public void fireProp1Changed(NestedStruct1 oldValue, NestedStruct1 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp1Changed(oldValue, newValue); - } - } - - @Override - public void fireProp2Changed(NestedStruct2 oldValue, NestedStruct2 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp2Changed(oldValue, newValue); - } - } - - @Override - public void fireProp3Changed(NestedStruct3 oldValue, NestedStruct3 newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.onProp3Changed(oldValue, newValue); - } - } - - @Override - public void fireSig1(NestedStruct1 param1) { - for (ISig1EventListener listener : events) { - listener.onSig1(param1); - } - } - - @Override - public void fireSig2(NestedStruct1 param1, NestedStruct2 param2) { - for (ISig2EventListener listener : events) { - listener.onSig2(param1, param2); - } - } - - @Override - public void fireSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { - for (ISig3EventListener listener : events) { - listener.onSig3(param1, param2, param3); - } - } - - - } -} \ No newline at end of file diff --git a/goldenmaster/testbed2/gradle.properties b/goldenmaster/testbed2/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/testbed2/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/testbed2/gradle/libs.versions.toml b/goldenmaster/testbed2/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/testbed2/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/testbed2/settings.gradle b/goldenmaster/testbed2/settings.gradle new file mode 100644 index 0000000..d99467d --- /dev/null +++ b/goldenmaster/testbed2/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "testbed2" +include ':testbed2_android_service' +include ':testbed2_android_client' +include ':testbed2_android_messenger' +include ':testbed2_impl' +include ':testbed2_api' +include ':testbed2_client_example' +include ':testbed2serviceexample' \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_android_client/additions.gradle b/goldenmaster/testbed2/testbed2_android_client/additions.gradle new file mode 100644 index 0000000..6f5a14d --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'testbed2.testbed2_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + implementation project(':testbed2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_android_client/build.gradle b/goldenmaster/testbed2/testbed2_android_client/build.gradle new file mode 100644 index 0000000..50dec0e --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed2" +version = "1.0.0" + +android { + namespace 'testbed2.testbed2_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + implementation project(':testbed2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java new file mode 100644 index 0000000..cc59b8f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java @@ -0,0 +1,737 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class ManyParamInterfaceClient extends AbstractManyParamInterface implements ServiceConnection +{ + private static final String TAG = "ManyParamInterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private int m_prop1 = 0; + private int m_prop2 = 0; + private int m_prop3 = 0; + private int m_prop4 = 0; + + + public ManyParamInterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type ManyParamInterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (ManyParamInterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + int prop1 = data.getInt("prop1", 0); + onProp1(prop1); + + int prop2 = data.getInt("prop2", 0); + onProp2(prop2); + + int prop3 = data.getInt("prop3", 0); + onProp3(prop3); + + int prop4 = data.getInt("prop4", 0); + onProp4(prop4); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + + + int prop1 = data.getInt("prop1", 0); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + + + int prop2 = data.getInt("prop2", 0); + + onProp2(prop2); + break; + } + case SET_Prop3: + { + Bundle data = msg.getData(); + + + int prop3 = data.getInt("prop3", 0); + + onProp3(prop3); + break; + } + case SET_Prop4: + { + Bundle data = msg.getData(); + + + int prop4 = data.getInt("prop4", 0); + + onProp4(prop4); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + + int param1 = data.getInt("param1", 0); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + + int param1 = data.getInt("param1", 0); + + int param2 = data.getInt("param2", 0); + onSig2(param1, param2); + break; + } + case SIG_Sig3: { + + Bundle data = msg.getData(); + + int param1 = data.getInt("param1", 0); + + int param2 = data.getInt("param2", 0); + + int param3 = data.getInt("param3", 0); + onSig3(param1, param2, param3); + break; + } + case SIG_Sig4: { + + Bundle data = msg.getData(); + + int param1 = data.getInt("param1", 0); + + int param2 = data.getInt("param2", 0); + + int param3 = data.getInt("param3", 0); + + int param4 = data.getInt("param4", 0); + onSig4(param1, param2, param3, param4); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ManyParamInterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ManyParamInterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func3Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ManyParamInterfaceMessageType.RPC_Func3Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func4Resp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ManyParamInterfaceMessageType.RPC_Func4Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(int prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (m_prop1 != prop1) + { + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop1", prop1); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(int prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public int getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(int prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (m_prop2 != prop2) + { + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop2", prop2); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(int prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public int getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + @Override + public void setProp3(int prop3) + { + Log.i(TAG, "request setProp3 called "+ prop3); + if (m_prop3 != prop3) + { + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.PROP_Prop3.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop3", prop3); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp3(int prop3) + { + Log.i(TAG, "value received from service for Prop3 "); + if (m_prop3 != prop3) + { + m_prop3 = prop3; + fireProp3Changed(prop3); + } + + } + + @Override + public int getProp3() + { + Log.i(TAG, "request getProp3 called, returning local"); + return m_prop3; + } + + + @Override + public void setProp4(int prop4) + { + Log.i(TAG, "request setProp4 called "+ prop4); + if (m_prop4 != prop4) + { + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.PROP_Prop4.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop4", prop4); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp4(int prop4) + { + Log.i(TAG, "value received from service for Prop4 "); + if (m_prop4 != prop4) + { + m_prop4 = prop4; + fireProp4Changed(prop4); + } + + } + + @Override + public int getProp4() + { + Log.i(TAG, "request getProp4 called, returning local"); + return m_prop4; + } + + + // methods + + + @Override + public int func1(int param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(int param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("param1", param1); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int func2(int param1, int param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(int param1, int param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("param1", param1); + + data.putInt("param2", param2); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int func3(int param1, int param2, int param3) { + CompletableFuture resFuture = func3Async(param1, param2, param3); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func3Async(int param1, int param2, int param3) { + + Log.i(TAG, "Call on service func3 "+ " " + param1+ " " + param2+ " " + param3); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.RPC_Func3Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("param1", param1); + + data.putInt("param2", param2); + + data.putInt("param3", param3); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve func3" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public int func4(int param1, int param2, int param3, int param4) { + CompletableFuture resFuture = func4Async(param1, param2, param3, param4); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func4Async(int param1, int param2, int param3, int param4) { + + Log.i(TAG, "Call on service func4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.RPC_Func4Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("param1", param1); + + data.putInt("param2", param2); + + data.putInt("param3", param3); + + data.putInt("param4", param4); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve func4" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(int param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(int param1, int param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } + public void onSig3(int param1, int param2, int param3) + { + Log.i(TAG, "onSig3 received from service"); + fireSig3(param1, param2, param3); + } + public void onSig4(int param1, int param2, int param3, int param4) + { + Log.i(TAG, "onSig4 received from service"); + fireSig4(param1, param2, param3, param4); + } +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java new file mode 100644 index 0000000..4910d0d --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java @@ -0,0 +1,351 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NestedStruct1InterfaceClient extends AbstractNestedStruct1Interface implements ServiceConnection +{ + private static final String TAG = "NestedStruct1InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private NestedStruct1 m_prop1 = new NestedStruct1(); + + + public NestedStruct1InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NestedStruct1InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NestedStruct1InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + onProp1(prop1); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + onProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + onSig1(param1); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct1InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(NestedStruct1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + // methods + + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java new file mode 100644 index 0000000..8e41a12 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java @@ -0,0 +1,483 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NestedStruct2InterfaceClient extends AbstractNestedStruct2Interface implements ServiceConnection +{ + private static final String TAG = "NestedStruct2InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private NestedStruct1 m_prop1 = new NestedStruct1(); + private NestedStruct2 m_prop2 = new NestedStruct2(); + + + public NestedStruct2InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NestedStruct2InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NestedStruct2InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + onProp1(prop1); + + NestedStruct2 prop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + onProp2(prop2); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + + NestedStruct2 prop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + + onProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 param2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + onSig2(param1, param2); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct2InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct2InterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(NestedStruct1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (! m_prop2.equals(prop2)) + { + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new NestedStruct2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(NestedStruct2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + // methods + + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + + data.putParcelable("param2", new NestedStruct2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java new file mode 100644 index 0000000..adb2a9b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java @@ -0,0 +1,621 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class NestedStruct3InterfaceClient extends AbstractNestedStruct3Interface implements ServiceConnection +{ + private static final String TAG = "NestedStruct3InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private NestedStruct1 m_prop1 = new NestedStruct1(); + private NestedStruct2 m_prop2 = new NestedStruct2(); + private NestedStruct3 m_prop3 = new NestedStruct3(); + + + public NestedStruct3InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type NestedStruct3InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (NestedStruct3InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + onProp1(prop1); + + NestedStruct2 prop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + onProp2(prop2); + + NestedStruct3 prop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); + onProp3(prop3); + + break; + } + case SET_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + onProp1(prop1); + break; + } + case SET_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + + NestedStruct2 prop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + + onProp2(prop2); + break; + } + case SET_Prop3: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + + NestedStruct3 prop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); + + onProp3(prop3); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_Sig1: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + onSig1(param1); + break; + } + case SIG_Sig2: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 param2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + onSig2(param1, param2); + break; + } + case SIG_Sig3: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 param2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + + NestedStruct3 param3 = data.getParcelable("param3", NestedStruct3Parcelable.class).getNestedStruct3(); + onSig3(param1, param2, param3); + break; + } + case RPC_Func1Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct3InterfaceMessageType.RPC_Func1Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func2Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct3InterfaceMessageType.RPC_Func2Resp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_Func3Resp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct3InterfaceMessageType.RPC_Func3Resp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called "+ prop1); + if (! m_prop1.equals(prop1)) + { + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.PROP_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp1(NestedStruct1 prop1) + { + Log.i(TAG, "value received from service for Prop1 "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "request setProp2 called "+ prop2); + if (! m_prop2.equals(prop2)) + { + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.PROP_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new NestedStruct2Parcelable(prop2)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp2(NestedStruct2 prop2) + { + Log.i(TAG, "value received from service for Prop2 "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + fireProp2Changed(prop2); + } + + } + + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "request getProp2 called, returning local"); + return m_prop2; + } + + + @Override + public void setProp3(NestedStruct3 prop3) + { + Log.i(TAG, "request setProp3 called "+ prop3); + if (! m_prop3.equals(prop3)) + { + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.PROP_Prop3.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop3", new NestedStruct3Parcelable(prop3)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onProp3(NestedStruct3 prop3) + { + Log.i(TAG, "value received from service for Prop3 "); + if (! m_prop3.equals(prop3)) + { + m_prop3 = prop3; + fireProp3Changed(prop3); + } + + } + + @Override + public NestedStruct3 getProp3() + { + Log.i(TAG, "request getProp3 called, returning local"); + return m_prop3; + } + + + // methods + + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + CompletableFuture resFuture = func1Async(param1); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + + Log.i(TAG, "Call on service func1 "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.RPC_Func1Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve func1" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { + CompletableFuture resFuture = func2Async(param1, param2); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) { + + Log.i(TAG, "Call on service func2 "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.RPC_Func2Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + + data.putParcelable("param2", new NestedStruct2Parcelable(param2)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve func2" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + CompletableFuture resFuture = func3Async(param1, param2, param3); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture func3Async(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + + Log.i(TAG, "Call on service func3 "+ " " + param1+ " " + param2+ " " + param3); + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.RPC_Func3Req.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + + data.putParcelable("param2", new NestedStruct2Parcelable(param2)); + + data.putParcelable("param3", new NestedStruct3Parcelable(param3)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve func3" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1 received from service"); + fireSig1(param1); + } + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(TAG, "onSig2 received from service"); + fireSig2(param1, param2); + } + public void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + { + Log.i(TAG, "onSig3 received from service"); + fireSig3(param1, param2, param3); + } +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java new file mode 100644 index 0000000..7bbbdff --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java @@ -0,0 +1,582 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed2.testbed2_android_client; + +import testbed2.testbed2_android_client.ManyParamInterfaceClient; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IManyParamInterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class ManyParamInterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private ManyParamInterfaceClient testedClient; + private IManyParamInterfaceEventListener listenerMock = mock(IManyParamInterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IManyParamInterfaceClientMessageGetter serviceMessagesStorage = mock(IManyParamInterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(ManyParamInterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IManyParamInterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new ManyParamInterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed2.testbed2_android_service", "testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(ManyParamInterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + int testprop1 = 1; + data.putInt("prop1", testprop1); + int testprop2 = 1; + data.putInt("prop2", testprop2); + int testprop3 = 1; + data.putInt("prop3", testprop3); + int testprop4 = 1; + data.putInt("prop4", testprop4); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); + inOrderEventListener.verify(listenerMock,times(1)).onProp4Changed(testprop4); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + int testprop1 = 1; + data.putInt("prop1", testprop1); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); + } + + @Test + public void setPropertyRequestprop1() + { + int testprop1 = 1; + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + int receivedprop1 = data.getInt("prop1", 0); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + int testprop2 = 1; + data.putInt("prop2", testprop2); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); + } + + @Test + public void setPropertyRequestprop2() + { + int testprop2 = 1; + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + int receivedprop2 = data.getInt("prop2", 0); + assertEquals(receivedprop2, testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop3PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SET_Prop3.getValue()); + Bundle data = new Bundle(); + int testprop3 = 1; + data.putInt("prop3", testprop3); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); + } + + @Test + public void setPropertyRequestprop3() + { + int testprop3 = 1; + + testedClient.setProp3(testprop3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.PROP_Prop3.getValue(), response.what); + Bundle data = response.getData(); + + int receivedprop3 = data.getInt("prop3", 0); + assertEquals(receivedprop3, testprop3); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop4PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SET_Prop4.getValue()); + Bundle data = new Bundle(); + int testprop4 = 1; + data.putInt("prop4", testprop4); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp4Changed(testprop4); + } + + @Test + public void setPropertyRequestprop4() + { + int testprop4 = 1; + + testedClient.setProp4(testprop4); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.PROP_Prop4.getValue(), response.what); + Bundle data = response.getData(); + + int receivedprop4 = data.getInt("prop4", 0); + assertEquals(receivedprop4, testprop4); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + int testparam1 = 1; + data.putInt("param1", testparam1); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1(testparam1); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + int testparam1 = 1; + data.putInt("param1", testparam1); + int testparam2 = 1; + data.putInt("param2", testparam2); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2(testparam1, testparam2); + +} + @Test + public void whenNotifiedsig3() throws RemoteException + { + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SIG_Sig3.getValue()); + Bundle data = new Bundle(); + int testparam1 = 1; + data.putInt("param1", testparam1); + int testparam2 = 1; + data.putInt("param2", testparam2); + int testparam3 = 1; + data.putInt("param3", testparam3); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig3(testparam1, testparam2, testparam3); + +} + @Test + public void whenNotifiedsig4() throws RemoteException + { + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.SIG_Sig4.getValue()); + Bundle data = new Bundle(); + int testparam1 = 1; + data.putInt("param1", testparam1); + int testparam2 = 1; + data.putInt("param2", testparam2); + int testparam3 = 1; + data.putInt("param3", testparam3); + int testparam4 = 1; + data.putInt("param4", testparam4); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig4(testparam1, testparam2, testparam3, testparam4); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + int testparam1 = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ManyParamInterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + int testparam1 = 1; + int testparam2 = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ManyParamInterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + + int receivedparam2 = data.getInt("param2", 0); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc3Request() throws RemoteException { + + // Execute method + int testparam1 = 1; + int testparam2 = 1; + int testparam3 = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func3Async(testparam1, testparam2, testparam3); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ManyParamInterfaceMessageType.RPC_Func3Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + + int receivedparam2 = data.getInt("param2", 0); + assertEquals(receivedparam2, testparam2); + + int receivedparam3 = data.getInt("param3", 0); + assertEquals(receivedparam3, testparam3); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func3Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc4Request() throws RemoteException { + + // Execute method + int testparam1 = 1; + int testparam2 = 1; + int testparam3 = 1; + int testparam4 = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func4Async(testparam1, testparam2, testparam3, testparam4); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ManyParamInterfaceMessageType.RPC_Func4Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + + int receivedparam2 = data.getInt("param2", 0); + assertEquals(receivedparam2, testparam2); + + int receivedparam3 = data.getInt("param3", 0); + assertEquals(receivedparam3, testparam3); + + int receivedparam4 = data.getInt("param4", 0); + assertEquals(receivedparam4, testparam4); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func4Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java new file mode 100644 index 0000000..3b7ec74 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java @@ -0,0 +1,263 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed2.testbed2_android_client; + +import testbed2.testbed2_android_client.NestedStruct1InterfaceClient; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INestedStruct1InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NestedStruct1InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private NestedStruct1InterfaceClient testedClient; + private INestedStruct1InterfaceEventListener listenerMock = mock(INestedStruct1InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INestedStruct1InterfaceClientMessageGetter serviceMessagesStorage = mock(INestedStruct1InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NestedStruct1InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INestedStruct1InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NestedStruct1InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed2.testbed2_android_service", "testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NestedStruct1InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); + } + + @Test + public void setPropertyRequestprop1() + { + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(NestedStruct1.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java new file mode 100644 index 0000000..7fadcef --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java @@ -0,0 +1,367 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed2.testbed2_android_client; + +import testbed2.testbed2_android_client.NestedStruct2InterfaceClient; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INestedStruct2InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NestedStruct2InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private NestedStruct2InterfaceClient testedClient; + private INestedStruct2InterfaceEventListener listenerMock = mock(INestedStruct2InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INestedStruct2InterfaceClientMessageGetter serviceMessagesStorage = mock(INestedStruct2InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NestedStruct2InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INestedStruct2InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NestedStruct2InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed2.testbed2_android_service", "testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NestedStruct2InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("prop2", new NestedStruct2Parcelable(testprop2)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); + } + + @Test + public void setPropertyRequestprop1() + { + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("prop2", new NestedStruct2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); + } + + @Test + public void setPropertyRequestprop2() + { + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(NestedStruct1.class)); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("param2", new NestedStruct2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2( any(NestedStruct1.class), any(NestedStruct2.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + + NestedStruct2 receivedparam2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java new file mode 100644 index 0000000..8baaf0b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java @@ -0,0 +1,478 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed2.testbed2_android_client; + +import testbed2.testbed2_android_client.NestedStruct3InterfaceClient; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface INestedStruct3InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NestedStruct3InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private NestedStruct3InterfaceClient testedClient; + private INestedStruct3InterfaceEventListener listenerMock = mock(INestedStruct3InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private INestedStruct3InterfaceClientMessageGetter serviceMessagesStorage = mock(INestedStruct3InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NestedStruct3InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(INestedStruct3InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new NestedStruct3InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed2.testbed2_android_service", "testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(NestedStruct3InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("prop2", new NestedStruct2Parcelable(testprop2)); + NestedStruct3 testprop3 = Testbed2TestHelper.makeTestNestedStruct3(); + data.putParcelable("prop3", new NestedStruct3Parcelable(testprop3)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); + inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(any(NestedStruct3.class)); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.SET_Prop1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); + } + + @Test + public void setPropertyRequestprop1() + { + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedClient.setProp1(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.PROP_Prop1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.SET_Prop2.getValue()); + Bundle data = new Bundle(); + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("prop2", new NestedStruct2Parcelable(testprop2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); + } + + @Test + public void setPropertyRequestprop2() + { + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + + testedClient.setProp2(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.PROP_Prop2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedprop2, testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop3PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.SET_Prop3.getValue()); + Bundle data = new Bundle(); + NestedStruct3 testprop3 = Testbed2TestHelper.makeTestNestedStruct3(); + data.putParcelable("prop3", new NestedStruct3Parcelable(testprop3)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(any(NestedStruct3.class)); + } + + @Test + public void setPropertyRequestprop3() + { + NestedStruct3 testprop3 = Testbed2TestHelper.makeTestNestedStruct3(); + + testedClient.setProp3(testprop3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.PROP_Prop3.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + NestedStruct3 receivedprop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); + assertEquals(receivedprop3, testprop3); + } + @Test + public void whenNotifiedsig1() throws RemoteException + { + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.SIG_Sig1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig1( any(NestedStruct1.class)); + +} + @Test + public void whenNotifiedsig2() throws RemoteException + { + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.SIG_Sig2.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("param2", new NestedStruct2Parcelable(testparam2)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig2( any(NestedStruct1.class), any(NestedStruct2.class)); + +} + @Test + public void whenNotifiedsig3() throws RemoteException + { + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.SIG_Sig3.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("param2", new NestedStruct2Parcelable(testparam2)); + NestedStruct3 testparam3 = Testbed2TestHelper.makeTestNestedStruct3(); + data.putParcelable("param3", new NestedStruct3Parcelable(testparam3)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSig3( any(NestedStruct1.class), any(NestedStruct2.class), any(NestedStruct3.class)); + +} + + + public void onfunc1Request() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func1Async(testparam1); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct3InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.RPC_Func1Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc2Request() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func2Async(testparam1, testparam2); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct3InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + + NestedStruct2 receivedparam2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedparam2, testparam2); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.RPC_Func2Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfunc3Request() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + NestedStruct3 testparam3 = Testbed2TestHelper.makeTestNestedStruct3(); + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.func3Async(testparam1, testparam2, testparam3); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct3InterfaceMessageType.RPC_Func3Req.getValue(), method_request.what); + Bundle data = method_request.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + + NestedStruct2 receivedparam2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedparam2, testparam2); + + NestedStruct3 receivedparam3 = data.getParcelable("param3", NestedStruct3Parcelable.class).getNestedStruct3(); + assertEquals(receivedparam3, testparam3); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.RPC_Func3Resp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/additions.gradle b/goldenmaster/testbed2/testbed2_android_messenger/additions.gradle new file mode 100644 index 0000000..1d96383 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'testbed2.testbed2_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/build.gradle b/goldenmaster/testbed2/testbed2_android_messenger/build.gradle new file mode 100644 index 0000000..a9fc843 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed2" +version = "1.0.0" + +android { + namespace 'testbed2.testbed2_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum1Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum1Parcelable.java new file mode 100644 index 0000000..20916ae --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum1Parcelable.java @@ -0,0 +1,69 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Enum1; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum1Parcelable implements Parcelable { + + public Enum1 data; + + public Enum1Parcelable(Enum1 data) { + this.data = data; + } + + public Enum1 getEnum1() + { + return data; + } + + protected Enum1Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum1.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum1Parcelable createFromParcel(Parcel in) { + return new Enum1Parcelable(in); + } + + @Override + public Enum1Parcelable[] newArray(int size) { + return new Enum1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum1Parcelable[] wrapArray(Enum1[] enums) { + if (enums == null) return null; + Enum1Parcelable[] result = new Enum1Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum1Parcelable(enums[i]); + } + return result; + } + + public static Enum1[] unwrapArray(Enum1Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum1[] out = new Enum1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum2Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum2Parcelable.java new file mode 100644 index 0000000..73785c0 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum2Parcelable.java @@ -0,0 +1,69 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Enum2; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum2Parcelable implements Parcelable { + + public Enum2 data; + + public Enum2Parcelable(Enum2 data) { + this.data = data; + } + + public Enum2 getEnum2() + { + return data; + } + + protected Enum2Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum2.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum2Parcelable createFromParcel(Parcel in) { + return new Enum2Parcelable(in); + } + + @Override + public Enum2Parcelable[] newArray(int size) { + return new Enum2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum2Parcelable[] wrapArray(Enum2[] enums) { + if (enums == null) return null; + Enum2Parcelable[] result = new Enum2Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum2Parcelable(enums[i]); + } + return result; + } + + public static Enum2[] unwrapArray(Enum2Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum2[] out = new Enum2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum3Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum3Parcelable.java new file mode 100644 index 0000000..324ae7b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Enum3Parcelable.java @@ -0,0 +1,69 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Enum3; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum3Parcelable implements Parcelable { + + public Enum3 data; + + public Enum3Parcelable(Enum3 data) { + this.data = data; + } + + public Enum3 getEnum3() + { + return data; + } + + protected Enum3Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum3.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum3Parcelable createFromParcel(Parcel in) { + return new Enum3Parcelable(in); + } + + @Override + public Enum3Parcelable[] newArray(int size) { + return new Enum3Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum3Parcelable[] wrapArray(Enum3[] enums) { + if (enums == null) return null; + Enum3Parcelable[] result = new Enum3Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum3Parcelable(enums[i]); + } + return result; + } + + public static Enum3[] unwrapArray(Enum3Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum3[] out = new Enum3[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum3(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceMessageType.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceMessageType.java new file mode 100644 index 0000000..17746c6 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceMessageType.java @@ -0,0 +1,50 @@ +package testbed2.testbed2_android_messenger; + +public enum ManyParamInterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + PROP_Prop3(7), + SET_Prop3(8), + PROP_Prop4(9), + SET_Prop4(10), + SIG_Sig1(11), + SIG_Sig2(12), + SIG_Sig3(13), + SIG_Sig4(14), + RPC_Func1Req(15), + RPC_Func1Resp(16), + RPC_Func2Req(17), + RPC_Func2Resp(18), + RPC_Func3Req(19), + RPC_Func3Resp(20), + RPC_Func4Req(21), + RPC_Func4Resp(22), + ManyParamInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + ManyParamInterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static ManyParamInterfaceMessageType fromInteger(int value) + { + for (ManyParamInterfaceMessageType event : ManyParamInterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return ManyParamInterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java new file mode 100644 index 0000000..943e958 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java @@ -0,0 +1,35 @@ +package testbed2.testbed2_android_messenger; + +public enum NestedStruct1InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + SIG_Sig1(5), + RPC_Func1Req(6), + RPC_Func1Resp(7), + NestedStruct1InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NestedStruct1InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NestedStruct1InterfaceMessageType fromInteger(int value) + { + for (NestedStruct1InterfaceMessageType event : NestedStruct1InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NestedStruct1InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1Parcelable.java new file mode 100644 index 0000000..f1f6e9b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1Parcelable.java @@ -0,0 +1,67 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.NestedStruct1; +import android.os.Parcel; +import android.os.Parcelable; +import testbed2.testbed2_api.Struct1; + + public class NestedStruct1Parcelable implements Parcelable { + + public NestedStruct1 data; + + public NestedStruct1Parcelable(NestedStruct1 data) { + this.data = new NestedStruct1(data); + } + + public NestedStruct1 getNestedStruct1() + { + return new NestedStruct1(data); + } + + protected NestedStruct1Parcelable(Parcel in) { + this.data = new NestedStruct1(); + Struct1Parcelable l_parcelablefield1 = in.readParcelable(Struct1Parcelable.class.getClassLoader(), Struct1Parcelable.class); + data.field1 = l_parcelablefield1 != null ? l_parcelablefield1.data : null; + } + + public static final Creator CREATOR = new Creator() { + @Override + public NestedStruct1Parcelable createFromParcel(Parcel in) { + return new NestedStruct1Parcelable(in); + } + + @Override + public NestedStruct1Parcelable[] newArray(int size) { + return new NestedStruct1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Struct1Parcelable(data.field1), flags); + + + } + public static NestedStruct1Parcelable[] wrapArray(NestedStruct1[] structs) { + if (structs == null) return null; + NestedStruct1Parcelable[] out = new NestedStruct1Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new NestedStruct1Parcelable(structs[i]); + } + return out; + } + + public static NestedStruct1[] unwrapArray(NestedStruct1Parcelable[] parcelables) { + if (parcelables == null) return null; + NestedStruct1[] out = new NestedStruct1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNestedStruct1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceMessageType.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceMessageType.java new file mode 100644 index 0000000..90f4017 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceMessageType.java @@ -0,0 +1,40 @@ +package testbed2.testbed2_android_messenger; + +public enum NestedStruct2InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + SIG_Sig1(7), + SIG_Sig2(8), + RPC_Func1Req(9), + RPC_Func1Resp(10), + RPC_Func2Req(11), + RPC_Func2Resp(12), + NestedStruct2InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NestedStruct2InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NestedStruct2InterfaceMessageType fromInteger(int value) + { + for (NestedStruct2InterfaceMessageType event : NestedStruct2InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NestedStruct2InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2Parcelable.java new file mode 100644 index 0000000..fdf1720 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2Parcelable.java @@ -0,0 +1,71 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.NestedStruct2; +import android.os.Parcel; +import android.os.Parcelable; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; + + public class NestedStruct2Parcelable implements Parcelable { + + public NestedStruct2 data; + + public NestedStruct2Parcelable(NestedStruct2 data) { + this.data = new NestedStruct2(data); + } + + public NestedStruct2 getNestedStruct2() + { + return new NestedStruct2(data); + } + + protected NestedStruct2Parcelable(Parcel in) { + this.data = new NestedStruct2(); + Struct1Parcelable l_parcelablefield1 = in.readParcelable(Struct1Parcelable.class.getClassLoader(), Struct1Parcelable.class); + data.field1 = l_parcelablefield1 != null ? l_parcelablefield1.data : null; + Struct2Parcelable l_parcelablefield2 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); + data.field2 = l_parcelablefield2 != null ? l_parcelablefield2.data : null; + } + + public static final Creator CREATOR = new Creator() { + @Override + public NestedStruct2Parcelable createFromParcel(Parcel in) { + return new NestedStruct2Parcelable(in); + } + + @Override + public NestedStruct2Parcelable[] newArray(int size) { + return new NestedStruct2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Struct1Parcelable(data.field1), flags); + dest.writeParcelable(new Struct2Parcelable(data.field2), flags); + + + } + public static NestedStruct2Parcelable[] wrapArray(NestedStruct2[] structs) { + if (structs == null) return null; + NestedStruct2Parcelable[] out = new NestedStruct2Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new NestedStruct2Parcelable(structs[i]); + } + return out; + } + + public static NestedStruct2[] unwrapArray(NestedStruct2Parcelable[] parcelables) { + if (parcelables == null) return null; + NestedStruct2[] out = new NestedStruct2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNestedStruct2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceMessageType.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceMessageType.java new file mode 100644 index 0000000..42bf0f0 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceMessageType.java @@ -0,0 +1,45 @@ +package testbed2.testbed2_android_messenger; + +public enum NestedStruct3InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Prop1(3), + SET_Prop1(4), + PROP_Prop2(5), + SET_Prop2(6), + PROP_Prop3(7), + SET_Prop3(8), + SIG_Sig1(9), + SIG_Sig2(10), + SIG_Sig3(11), + RPC_Func1Req(12), + RPC_Func1Resp(13), + RPC_Func2Req(14), + RPC_Func2Resp(15), + RPC_Func3Req(16), + RPC_Func3Resp(17), + NestedStruct3InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + NestedStruct3InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static NestedStruct3InterfaceMessageType fromInteger(int value) + { + for (NestedStruct3InterfaceMessageType event : NestedStruct3InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return NestedStruct3InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java new file mode 100644 index 0000000..83aca37 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java @@ -0,0 +1,83 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.NestedStruct3; +import android.os.Parcel; +import android.os.Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; + + public class NestedStruct3Parcelable implements Parcelable { + + public NestedStruct3 data; + + public NestedStruct3Parcelable(NestedStruct3 data) { + this.data = new NestedStruct3(data); + } + + public NestedStruct3 getNestedStruct3() + { + return new NestedStruct3(data); + } + + protected NestedStruct3Parcelable(Parcel in) { + this.data = new NestedStruct3(); + Enum1Parcelable l_parcelablefield1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + data.field1 = l_parcelablefield1 != null ? l_parcelablefield1.data : null; + Struct2Parcelable l_parcelablefield2 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); + data.field2 = l_parcelablefield2 != null ? l_parcelablefield2.data : null; + Struct3Parcelable l_parcelablefield3 = in.readParcelable(Struct3Parcelable.class.getClassLoader(), Struct3Parcelable.class); + data.field3 = l_parcelablefield3 != null ? l_parcelablefield3.data : null; + Enum1Parcelable[] l_parcelablefieldX = in.createTypedArray(Enum1Parcelable.CREATOR); + data.fieldX = Enum1Parcelable.unwrapArray(l_parcelablefieldX); + Struct2Parcelable[] l_parcelablefieldY = in.createTypedArray(Struct2Parcelable.CREATOR); + data.fieldY = Struct2Parcelable.unwrapArray(l_parcelablefieldY); + Struct3Parcelable[] l_parcelablefieldZ = in.createTypedArray(Struct3Parcelable.CREATOR); + data.fieldZ = Struct3Parcelable.unwrapArray(l_parcelablefieldZ); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NestedStruct3Parcelable createFromParcel(Parcel in) { + return new NestedStruct3Parcelable(in); + } + + @Override + public NestedStruct3Parcelable[] newArray(int size) { + return new NestedStruct3Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum1Parcelable(data.field1), flags); + dest.writeParcelable(new Struct2Parcelable(data.field2), flags); + dest.writeParcelable(new Struct3Parcelable(data.field3), flags); + dest.writeTypedArray(Enum1Parcelable.wrapArray(data.fieldX), flags); + dest.writeTypedArray(Struct2Parcelable.wrapArray(data.fieldY), flags); + dest.writeTypedArray(Struct3Parcelable.wrapArray(data.fieldZ), flags); + + + } + public static NestedStruct3Parcelable[] wrapArray(NestedStruct3[] structs) { + if (structs == null) return null; + NestedStruct3Parcelable[] out = new NestedStruct3Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new NestedStruct3Parcelable(structs[i]); + } + return out; + } + + public static NestedStruct3[] unwrapArray(NestedStruct3Parcelable[] parcelables) { + if (parcelables == null) return null; + NestedStruct3[] out = new NestedStruct3[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNestedStruct3(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct1Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct1Parcelable.java new file mode 100644 index 0000000..831055f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct1Parcelable.java @@ -0,0 +1,65 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Struct1; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct1Parcelable implements Parcelable { + + public Struct1 data; + + public Struct1Parcelable(Struct1 data) { + this.data = new Struct1(data); + } + + public Struct1 getStruct1() + { + return new Struct1(data); + } + + protected Struct1Parcelable(Parcel in) { + this.data = new Struct1(); + data.field1 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct1Parcelable createFromParcel(Parcel in) { + return new Struct1Parcelable(in); + } + + @Override + public Struct1Parcelable[] newArray(int size) { + return new Struct1Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + + + } + public static Struct1Parcelable[] wrapArray(Struct1[] structs) { + if (structs == null) return null; + Struct1Parcelable[] out = new Struct1Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct1Parcelable(structs[i]); + } + return out; + } + + public static Struct1[] unwrapArray(Struct1Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct1[] out = new Struct1[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct1(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct2Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct2Parcelable.java new file mode 100644 index 0000000..8225f97 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct2Parcelable.java @@ -0,0 +1,67 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Struct2; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct2Parcelable implements Parcelable { + + public Struct2 data; + + public Struct2Parcelable(Struct2 data) { + this.data = new Struct2(data); + } + + public Struct2 getStruct2() + { + return new Struct2(data); + } + + protected Struct2Parcelable(Parcel in) { + this.data = new Struct2(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct2Parcelable createFromParcel(Parcel in) { + return new Struct2Parcelable(in); + } + + @Override + public Struct2Parcelable[] newArray(int size) { + return new Struct2Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + + + } + public static Struct2Parcelable[] wrapArray(Struct2[] structs) { + if (structs == null) return null; + Struct2Parcelable[] out = new Struct2Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct2Parcelable(structs[i]); + } + return out; + } + + public static Struct2[] unwrapArray(Struct2Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct2[] out = new Struct2[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct2(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct3Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct3Parcelable.java new file mode 100644 index 0000000..c7a7c3a --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct3Parcelable.java @@ -0,0 +1,69 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Struct3; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct3Parcelable implements Parcelable { + + public Struct3 data; + + public Struct3Parcelable(Struct3 data) { + this.data = new Struct3(data); + } + + public Struct3 getStruct3() + { + return new Struct3(data); + } + + protected Struct3Parcelable(Parcel in) { + this.data = new Struct3(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + data.field3 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct3Parcelable createFromParcel(Parcel in) { + return new Struct3Parcelable(in); + } + + @Override + public Struct3Parcelable[] newArray(int size) { + return new Struct3Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + dest.writeInt(data.field3); + + + } + public static Struct3Parcelable[] wrapArray(Struct3[] structs) { + if (structs == null) return null; + Struct3Parcelable[] out = new Struct3Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct3Parcelable(structs[i]); + } + return out; + } + + public static Struct3[] unwrapArray(Struct3Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct3[] out = new Struct3[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct3(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct4Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct4Parcelable.java new file mode 100644 index 0000000..4fdc2e5 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/Struct4Parcelable.java @@ -0,0 +1,71 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.Struct4; +import android.os.Parcel; +import android.os.Parcelable; + + public class Struct4Parcelable implements Parcelable { + + public Struct4 data; + + public Struct4Parcelable(Struct4 data) { + this.data = new Struct4(data); + } + + public Struct4 getStruct4() + { + return new Struct4(data); + } + + protected Struct4Parcelable(Parcel in) { + this.data = new Struct4(); + data.field1 = in.readInt(); + data.field2 = in.readInt(); + data.field3 = in.readInt(); + data.field4 = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Struct4Parcelable createFromParcel(Parcel in) { + return new Struct4Parcelable(in); + } + + @Override + public Struct4Parcelable[] newArray(int size) { + return new Struct4Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.field1); + dest.writeInt(data.field2); + dest.writeInt(data.field3); + dest.writeInt(data.field4); + + + } + public static Struct4Parcelable[] wrapArray(Struct4[] structs) { + if (structs == null) return null; + Struct4Parcelable[] out = new Struct4Parcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Struct4Parcelable(structs[i]); + } + return out; + } + + public static Struct4[] unwrapArray(Struct4Parcelable[] parcelables) { + if (parcelables == null) return null; + Struct4[] out = new Struct4[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStruct4(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_service/additions.gradle b/goldenmaster/testbed2/testbed2_android_service/additions.gradle new file mode 100644 index 0000000..18e9927 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'testbed2.testbed2_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + implementation project(':testbed2_impl') + implementation project(':testbed2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_android_service/build.gradle b/goldenmaster/testbed2/testbed2_android_service/build.gradle new file mode 100644 index 0000000..cb34023 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed2" +version = "1.0.0" + + +android { + namespace 'testbed2.testbed2_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + implementation project(':testbed2_impl') + implementation project(':testbed2_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/AndroidManifest.xml b/goldenmaster/testbed2/testbed2_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..05ffeed --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/IManyParamInterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/IManyParamInterfaceServiceFactory.java new file mode 100644 index 0000000..0a76752 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/IManyParamInterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed2.testbed2_android_service; +import testbed2.testbed2_api.IManyParamInterface; + + +public interface IManyParamInterfaceServiceFactory { + public IManyParamInterface getServiceInstance(); +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct1InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct1InterfaceServiceFactory.java new file mode 100644 index 0000000..8c73660 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct1InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed2.testbed2_android_service; +import testbed2.testbed2_api.INestedStruct1Interface; + + +public interface INestedStruct1InterfaceServiceFactory { + public INestedStruct1Interface getServiceInstance(); +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct2InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct2InterfaceServiceFactory.java new file mode 100644 index 0000000..1d5a4f7 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct2InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed2.testbed2_android_service; +import testbed2.testbed2_api.INestedStruct2Interface; + + +public interface INestedStruct2InterfaceServiceFactory { + public INestedStruct2Interface getServiceInstance(); +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct3InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct3InterfaceServiceFactory.java new file mode 100644 index 0000000..c5b7cf2 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/INestedStruct3InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed2.testbed2_android_service; +import testbed2.testbed2_api.INestedStruct3Interface; + + +public interface INestedStruct3InterfaceServiceFactory { + public INestedStruct3Interface getServiceInstance(); +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java new file mode 100644 index 0000000..f505294 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java @@ -0,0 +1,520 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_android_service.IManyParamInterfaceServiceFactory; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class ManyParamInterfaceServiceAdapter extends Service +{ + private static final String TAG = "ManyParamInterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IManyParamInterface mBackendService; + private static IManyParamInterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public ManyParamInterfaceServiceAdapter() + { + } + + public static IManyParamInterface setService(IManyParamInterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(ManyParamInterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: ManyParamInterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(ManyParamInterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(ManyParamInterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IManyParamInterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (ManyParamInterfaceMessageType.fromInteger(msg.what) != ManyParamInterfaceMessageType.REGISTER_CLIENT + && ManyParamInterfaceMessageType.fromInteger(msg.what) != ManyParamInterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: ManyParamInterfaceMessageType" + ManyParamInterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (ManyParamInterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + + int prop1 = data.getInt("prop1", 0); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + + int prop2 = data.getInt("prop2", 0); + mBackendService.setProp2(prop2); + break; + } + case PROP_Prop3: + { + Bundle data = msg.getData(); + + int prop3 = data.getInt("prop3", 0); + mBackendService.setProp3(prop3); + break; + } + case PROP_Prop4: + { + Bundle data = msg.getData(); + + int prop4 = data.getInt("prop4", 0); + mBackendService.setProp4(prop4); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int param1 = data.getInt("param1", 0); + + int result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = ManyParamInterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int param1 = data.getInt("param1", 0); + + int param2 = data.getInt("param2", 0); + + int result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = ManyParamInterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func3Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int param1 = data.getInt("param1", 0); + + int param2 = data.getInt("param2", 0); + + int param3 = data.getInt("param3", 0); + + int result = mBackendService.func3(param1, param2, param3); + + Message respMsg = new Message(); + respMsg.what = ManyParamInterfaceMessageType.RPC_Func3Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func4Req: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + int param1 = data.getInt("param1", 0); + + int param2 = data.getInt("param2", 0); + + int param3 = data.getInt("param3", 0); + + int param4 = data.getInt("param4", 0); + + int result = mBackendService.func4(param1, param2, param3, param4); + + Message respMsg = new Message(); + respMsg.what = ManyParamInterfaceMessageType.RPC_Func4Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + int prop1 = mBackendService.getProp1(); + + data.putInt("prop1", prop1); + int prop2 = mBackendService.getProp2(); + + data.putInt("prop2", prop2); + int prop3 = mBackendService.getProp3(); + + data.putInt("prop3", prop3); + int prop4 = mBackendService.getProp4(); + + data.putInt("prop4", prop4); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(int prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop1", prop1); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(int prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop2", prop2); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp3Changed(int prop3){ + Log.i(TAG, "New value for Prop3 from backend" + prop3); + + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SET_Prop3.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop3", prop3); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp4Changed(int prop4){ + Log.i(TAG, "New value for Prop4 from backend" + prop4); + + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SET_Prop4.getValue(); + Bundle data = new Bundle(); + + data.putInt("prop4", prop4); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(int param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putInt("param1", param1); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(int param1, int param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putInt("param1", param1); + + data.putInt("param2", param2); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig3(int param1, int param2, int param3){ + Log.i(TAG, "New singal for Sig3 = "+ " " + param1+ " " + param2+ " " + param3); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SIG_Sig3.getValue(); + Bundle data = new Bundle(); + + data.putInt("param1", param1); + + data.putInt("param2", param2); + + data.putInt("param3", param3); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig4(int param1, int param2, int param3, int param4){ + Log.i(TAG, "New singal for Sig4 = "+ " " + param1+ " " + param2+ " " + param3+ " " + param4); + Message msg = new Message(); + msg.what = ManyParamInterfaceMessageType.SIG_Sig4.getValue(); + Bundle data = new Bundle(); + + data.putInt("param1", param1); + + data.putInt("param2", param2); + + data.putInt("param3", param3); + + data.putInt("param4", param4); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java new file mode 100644 index 0000000..46f25f9 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import testbed2.testbed2_android_service.IManyParamInterfaceServiceFactory; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton ManyParamInterfaceServiceFactory thread for the system. This is a thread for + * ManyParamInterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class ManyParamInterfaceServiceFactory extends HandlerThread implements IManyParamInterfaceServiceFactory +{ + private ManyParamInterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static ManyParamInterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: ManyParamInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractManyParamInterface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new ManyParamInterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new ManyParamInterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final ManyParamInterfaceServiceFactory INSTANCE = createInstance(); + } + + private ManyParamInterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static ManyParamInterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + ManyParamInterfaceServiceFactory t = new ManyParamInterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java new file mode 100644 index 0000000..1742363 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter; +import testbed2.testbed2_android_service.ManyParamInterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed2.testbed2_impl; . +public class ManyParamInterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "ManyParamInterfaceStarter"; + + + + public static IManyParamInterface start(Context context) { + stop(context); + androidService = new Intent(context, ManyParamInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + ManyParamInterfaceServiceFactory factory = ManyParamInterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for ManyParamInterfaceServiceFactory"); + return ManyParamInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java new file mode 100644 index 0000000..e816caa --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java @@ -0,0 +1,312 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_android_service.INestedStruct1InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NestedStruct1InterfaceServiceAdapter extends Service +{ + private static final String TAG = "NestedStruct1InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INestedStruct1Interface mBackendService; + private static INestedStruct1InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NestedStruct1InterfaceServiceAdapter() + { + } + + public static INestedStruct1Interface setService(INestedStruct1InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct1InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NestedStruct1InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct1InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INestedStruct1InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NestedStruct1InterfaceMessageType.fromInteger(msg.what) != NestedStruct1InterfaceMessageType.REGISTER_CLIENT + && NestedStruct1InterfaceMessageType.fromInteger(msg.what) != NestedStruct1InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NestedStruct1InterfaceMessageType" + NestedStruct1InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NestedStruct1InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + mBackendService.setProp1(prop1); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = NestedStruct1InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + NestedStruct1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(NestedStruct1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(NestedStruct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java new file mode 100644 index 0000000..d7fc31e --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import testbed2.testbed2_android_service.INestedStruct1InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NestedStruct1InterfaceServiceFactory thread for the system. This is a thread for + * NestedStruct1InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NestedStruct1InterfaceServiceFactory extends HandlerThread implements INestedStruct1InterfaceServiceFactory +{ + private NestedStruct1InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NestedStruct1InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NestedStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNestedStruct1Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NestedStruct1InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NestedStruct1InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NestedStruct1InterfaceServiceFactory INSTANCE = createInstance(); + } + + private NestedStruct1InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NestedStruct1InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NestedStruct1InterfaceServiceFactory t = new NestedStruct1InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java new file mode 100644 index 0000000..839e632 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter; +import testbed2.testbed2_android_service.NestedStruct1InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed2.testbed2_impl; . +public class NestedStruct1InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NestedStruct1InterfaceStarter"; + + + + public static INestedStruct1Interface start(Context context) { + stop(context); + androidService = new Intent(context, NestedStruct1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NestedStruct1InterfaceServiceFactory factory = NestedStruct1InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for NestedStruct1InterfaceServiceFactory"); + return NestedStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java new file mode 100644 index 0000000..e7f5ac7 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java @@ -0,0 +1,381 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_android_service.INestedStruct2InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NestedStruct2InterfaceServiceAdapter extends Service +{ + private static final String TAG = "NestedStruct2InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INestedStruct2Interface mBackendService; + private static INestedStruct2InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NestedStruct2InterfaceServiceAdapter() + { + } + + public static INestedStruct2Interface setService(INestedStruct2InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct2InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NestedStruct2InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct2InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INestedStruct2InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NestedStruct2InterfaceMessageType.fromInteger(msg.what) != NestedStruct2InterfaceMessageType.REGISTER_CLIENT + && NestedStruct2InterfaceMessageType.fromInteger(msg.what) != NestedStruct2InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NestedStruct2InterfaceMessageType" + NestedStruct2InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NestedStruct2InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct2 prop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + mBackendService.setProp2(prop2); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = NestedStruct2InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 param2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + + NestedStruct1 result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = NestedStruct2InterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + NestedStruct1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + NestedStruct2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new NestedStruct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(NestedStruct1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(NestedStruct2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new NestedStruct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(NestedStruct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(NestedStruct1 param1, NestedStruct2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = NestedStruct2InterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + + data.putParcelable("param2", new NestedStruct2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java new file mode 100644 index 0000000..7bf316e --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import testbed2.testbed2_android_service.INestedStruct2InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NestedStruct2InterfaceServiceFactory thread for the system. This is a thread for + * NestedStruct2InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NestedStruct2InterfaceServiceFactory extends HandlerThread implements INestedStruct2InterfaceServiceFactory +{ + private NestedStruct2InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NestedStruct2InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NestedStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNestedStruct2Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NestedStruct2InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NestedStruct2InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NestedStruct2InterfaceServiceFactory INSTANCE = createInstance(); + } + + private NestedStruct2InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NestedStruct2InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NestedStruct2InterfaceServiceFactory t = new NestedStruct2InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java new file mode 100644 index 0000000..fa4bd59 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter; +import testbed2.testbed2_android_service.NestedStruct2InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed2.testbed2_impl; . +public class NestedStruct2InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NestedStruct2InterfaceStarter"; + + + + public static INestedStruct2Interface start(Context context) { + stop(context); + androidService = new Intent(context, NestedStruct2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NestedStruct2InterfaceServiceFactory factory = NestedStruct2InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for NestedStruct2InterfaceServiceFactory"); + return NestedStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java new file mode 100644 index 0000000..d6656f0 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java @@ -0,0 +1,455 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_android_service.INestedStruct3InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class NestedStruct3InterfaceServiceAdapter extends Service +{ + private static final String TAG = "NestedStruct3InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static INestedStruct3Interface mBackendService; + private static INestedStruct3InterfaceServiceFactory mServiceFactory; + + //private final List mMessagesQueue = new ArrayList<>(); + + public NestedStruct3InterfaceServiceAdapter() + { + } + + public static INestedStruct3Interface setService(INestedStruct3InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + mBackendService.addEventListener(mHandler); + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct3InterfaceService) called. context = " + this); + + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + mBackendService.addEventListener(mHandler); + } + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: NestedStruct3InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + super.onDestroy(); + + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct3InterfaceService) - proc = " + ", mMessenger = " + mMessenger + ); + + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct3InterfaceService) - proc = " + ", remove engine event callback!"); + + mBackendService.removeEventListener(mHandler); + mBackendService = null; + } + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements INestedStruct3InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (NestedStruct3InterfaceMessageType.fromInteger(msg.what) != NestedStruct3InterfaceMessageType.REGISTER_CLIENT + && NestedStruct3InterfaceMessageType.fromInteger(msg.what) != NestedStruct3InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: NestedStruct3InterfaceMessageType" + NestedStruct3InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (NestedStruct3InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Prop1: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + mBackendService.setProp1(prop1); + break; + } + case PROP_Prop2: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct2 prop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + mBackendService.setProp2(prop2); + break; + } + case PROP_Prop3: + { + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + NestedStruct3 prop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); + mBackendService.setProp3(prop3); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func1Req: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct1 result = mBackendService.func1(param1); + + Message respMsg = new Message(); + respMsg.what = NestedStruct3InterfaceMessageType.RPC_Func1Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func2Req: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 param2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + + NestedStruct1 result = mBackendService.func2(param1, param2); + + Message respMsg = new Message(); + respMsg.what = NestedStruct3InterfaceMessageType.RPC_Func2Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_Func3Req: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 param2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + + NestedStruct3 param3 = data.getParcelable("param3", NestedStruct3Parcelable.class).getNestedStruct3(); + + NestedStruct1 result = mBackendService.func3(param1, param2, param3); + + Message respMsg = new Message(); + respMsg.what = NestedStruct3InterfaceMessageType.RPC_Func3Resp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + NestedStruct1 prop1 = mBackendService.getProp1(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + NestedStruct2 prop2 = mBackendService.getProp2(); + + data.putParcelable("prop2", new NestedStruct2Parcelable(prop2)); + NestedStruct3 prop3 = mBackendService.getProp3(); + + data.putParcelable("prop3", new NestedStruct3Parcelable(prop3)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp1Changed(NestedStruct1 prop1){ + Log.i(TAG, "New value for Prop1 from backend" + prop1); + + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.SET_Prop1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop1", new NestedStruct1Parcelable(prop1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp2Changed(NestedStruct2 prop2){ + Log.i(TAG, "New value for Prop2 from backend" + prop2); + + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.SET_Prop2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop2", new NestedStruct2Parcelable(prop2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onProp3Changed(NestedStruct3 prop3){ + Log.i(TAG, "New value for Prop3 from backend" + prop3); + + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.SET_Prop3.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("prop3", new NestedStruct3Parcelable(prop3)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig1(NestedStruct1 param1){ + Log.i(TAG, "New singal for Sig1 = "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.SIG_Sig1.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig2(NestedStruct1 param1, NestedStruct2 param2){ + Log.i(TAG, "New singal for Sig2 = "+ " " + param1+ " " + param2); + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.SIG_Sig2.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + + data.putParcelable("param2", new NestedStruct2Parcelable(param2)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3){ + Log.i(TAG, "New singal for Sig3 = "+ " " + param1+ " " + param2+ " " + param3); + Message msg = new Message(); + msg.what = NestedStruct3InterfaceMessageType.SIG_Sig3.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + + data.putParcelable("param2", new NestedStruct2Parcelable(param2)); + + data.putParcelable("param3", new NestedStruct3Parcelable(param3)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java new file mode 100644 index 0000000..ff596cb --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import testbed2.testbed2_android_service.INestedStruct3InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NestedStruct3InterfaceServiceFactory thread for the system. This is a thread for + * NestedStruct3InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NestedStruct3InterfaceServiceFactory extends HandlerThread implements INestedStruct3InterfaceServiceFactory +{ + private NestedStruct3InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NestedStruct3InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NestedStruct3InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNestedStruct3Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new NestedStruct3InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NestedStruct3InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final NestedStruct3InterfaceServiceFactory INSTANCE = createInstance(); + } + + private NestedStruct3InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NestedStruct3InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NestedStruct3InterfaceServiceFactory t = new NestedStruct3InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java new file mode 100644 index 0000000..3f5d08b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter; +import testbed2.testbed2_android_service.NestedStruct3InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed2.testbed2_impl; . +public class NestedStruct3InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NestedStruct3InterfaceStarter"; + + + + public static INestedStruct3Interface start(Context context) { + stop(context); + androidService = new Intent(context, NestedStruct3InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NestedStruct3InterfaceServiceFactory factory = NestedStruct3InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for NestedStruct3InterfaceServiceFactory"); + return NestedStruct3InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java new file mode 100644 index 0000000..896d670 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java @@ -0,0 +1,613 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_android_service.IManyParamInterfaceServiceFactory; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IManyParamInterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class ManyParamInterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private ManyParamInterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IManyParamInterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IManyParamInterface backendServiceMock = mock(IManyParamInterface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IManyParamInterfaceServiceFactory serviceFactory = mock(IManyParamInterfaceServiceFactory.class); + private IManyParamInterfaceMessageGetter clientMessagesStorage = mock(IManyParamInterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, ManyParamInterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IManyParamInterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + int initprop1 = 1; + when(backendServiceMock.getProp1()).thenReturn(initprop1); + int initprop2 = 1; + when(backendServiceMock.getProp2()).thenReturn(initprop2); + int initprop3 = 1; + when(backendServiceMock.getProp3()).thenReturn(initprop3); + int initprop4 = 1; + when(backendServiceMock.getProp4()).thenReturn(initprop4); + + + Message registerMsg = Message.obtain(null, ManyParamInterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp3(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp4(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + int receivedprop1 = data.getInt("prop1", 0); + + int receivedprop2 = data.getInt("prop2", 0); + + int receivedprop3 = data.getInt("prop3", 0); + + int receivedprop4 = data.getInt("prop4", 0); + assertEquals(receivedprop1, initprop1); + assertEquals(receivedprop2, initprop2); + assertEquals(receivedprop3, initprop3); + assertEquals(receivedprop4, initprop4); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, ManyParamInterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(ManyParamInterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IManyParamInterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + int testprop1 = 1; + data.putInt("prop1", testprop1); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1(testprop1); + + } + + @Test + public void whenNotifiedprop1() + { + int testprop1 = 1; + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedprop1 = data.getInt("prop1", 0); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + int testprop2 = 1; + data.putInt("prop2", testprop2); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2(testprop2); + + } + + @Test + public void whenNotifiedprop2() + { + int testprop2 = 1; + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedprop2 = data.getInt("prop2", 0); + + assertEquals(receivedprop2, testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop3PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.PROP_Prop3.getValue()); + Bundle data = new Bundle(); + int testprop3 = 1; + data.putInt("prop3", testprop3); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp3(testprop3); + + } + + @Test + public void whenNotifiedprop3() + { + int testprop3 = 1; + + testedAdapterAsEventListener.onProp3Changed(testprop3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SET_Prop3.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedprop3 = data.getInt("prop3", 0); + + assertEquals(receivedprop3, testprop3); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop4PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.PROP_Prop4.getValue()); + Bundle data = new Bundle(); + int testprop4 = 1; + data.putInt("prop4", testprop4); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp4(testprop4); + + } + + @Test + public void whenNotifiedprop4() + { + int testprop4 = 1; + + testedAdapterAsEventListener.onProp4Changed(testprop4); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SET_Prop4.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedprop4 = data.getInt("prop4", 0); + + assertEquals(receivedprop4, testprop4); + } + @Test + public void whenNotifiedsig1() + { + int testparam1 = 1; + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + int testparam1 = 1; + int testparam2 = 1; + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + + int receivedparam2 = data.getInt("param2", 0); + assertEquals(receivedparam2, testparam2); +} + @Test + public void whenNotifiedsig3() + { + int testparam1 = 1; + int testparam2 = 1; + int testparam3 = 1; + + testedAdapterAsEventListener.onSig3(testparam1, testparam2, testparam3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SIG_Sig3.getValue(), response.what); + Bundle data = response.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + + int receivedparam2 = data.getInt("param2", 0); + assertEquals(receivedparam2, testparam2); + + int receivedparam3 = data.getInt("param3", 0); + assertEquals(receivedparam3, testparam3); +} + @Test + public void whenNotifiedsig4() + { + int testparam1 = 1; + int testparam2 = 1; + int testparam3 = 1; + int testparam4 = 1; + + testedAdapterAsEventListener.onSig4(testparam1, testparam2, testparam3, testparam4); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.SIG_Sig4.getValue(), response.what); + Bundle data = response.getData(); + + int receivedparam1 = data.getInt("param1", 0); + assertEquals(receivedparam1, testparam1); + + int receivedparam2 = data.getInt("param2", 0); + assertEquals(receivedparam2, testparam2); + + int receivedparam3 = data.getInt("param3", 0); + assertEquals(receivedparam3, testparam3); + + int receivedparam4 = data.getInt("param4", 0); + assertEquals(receivedparam4, testparam4); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparam1 = 1; + data.putInt("param1", testparam1); + int returnedValue = 1; + + + when(backendServiceMock.func1(testparam1)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1(testparam1); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparam1 = 1; + data.putInt("param1", testparam1); + int testparam2 = 1; + data.putInt("param2", testparam2); + int returnedValue = 1; + + + when(backendServiceMock.func2(testparam1, testparam2)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2(testparam1, testparam2); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc3Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func3Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparam1 = 1; + data.putInt("param1", testparam1); + int testparam2 = 1; + data.putInt("param2", testparam2); + int testparam3 = 1; + data.putInt("param3", testparam3); + int returnedValue = 1; + + + when(backendServiceMock.func3(testparam1, testparam2, testparam3)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func3(testparam1, testparam2, testparam3); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.RPC_Func3Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc4Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ManyParamInterfaceMessageType.RPC_Func4Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparam1 = 1; + data.putInt("param1", testparam1); + int testparam2 = 1; + data.putInt("param2", testparam2); + int testparam3 = 1; + data.putInt("param3", testparam3); + int testparam4 = 1; + data.putInt("param4", testparam4); + int returnedValue = 1; + + + when(backendServiceMock.func4(testparam1, testparam2, testparam3, testparam4)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func4(testparam1, testparam2, testparam3, testparam4); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ManyParamInterfaceMessageType.RPC_Func4Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..5b3b865 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java @@ -0,0 +1,308 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_android_service.INestedStruct1InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INestedStruct1InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NestedStruct1InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NestedStruct1InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INestedStruct1InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INestedStruct1Interface backendServiceMock = mock(INestedStruct1Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INestedStruct1InterfaceServiceFactory serviceFactory = mock(INestedStruct1InterfaceServiceFactory.class); + private INestedStruct1InterfaceMessageGetter clientMessagesStorage = mock(INestedStruct1InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NestedStruct1InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INestedStruct1InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + NestedStruct1 initprop1 = new NestedStruct1(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + + + Message registerMsg = Message.obtain(null, NestedStruct1InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NestedStruct1InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NestedStruct1InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INestedStruct1InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(NestedStruct1.class)); + + } + + @Test + public void whenNotifiedprop1() + { + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedprop1, testprop1); + } + @Test + public void whenNotifiedsig1() + { + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.func1( any(NestedStruct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(NestedStruct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..afc88de --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java @@ -0,0 +1,410 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_android_service.INestedStruct2InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INestedStruct2InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NestedStruct2InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NestedStruct2InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INestedStruct2InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INestedStruct2Interface backendServiceMock = mock(INestedStruct2Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INestedStruct2InterfaceServiceFactory serviceFactory = mock(INestedStruct2InterfaceServiceFactory.class); + private INestedStruct2InterfaceMessageGetter clientMessagesStorage = mock(INestedStruct2InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NestedStruct2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INestedStruct2InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + NestedStruct1 initprop1 = new NestedStruct1(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + NestedStruct2 initprop2 = new NestedStruct2(); + //TODO fill fields + when(backendServiceMock.getProp2()).thenReturn(initprop2); + + + Message registerMsg = Message.obtain(null, NestedStruct2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + // assertEquals(receivedprop2, initprop2); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NestedStruct2InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NestedStruct2InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INestedStruct2InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(NestedStruct1.class)); + + } + + @Test + public void whenNotifiedprop1() + { + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("prop2", new NestedStruct2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2( any(NestedStruct2.class)); + + } + + @Test + public void whenNotifiedprop2() + { + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + + assertEquals(receivedprop2, testprop2); + } + @Test + public void whenNotifiedsig1() + { + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + + NestedStruct2 receivedparam2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedparam2, testparam2); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.func1( any(NestedStruct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(NestedStruct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct2InterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("param2", new NestedStruct2Parcelable(testparam2)); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.func2( any(NestedStruct1.class), any(NestedStruct2.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2( any(NestedStruct1.class), any(NestedStruct2.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct2InterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..1cc01c4 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java @@ -0,0 +1,519 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Testbed2TestHelper; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_android_service.INestedStruct3InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface INestedStruct3InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NestedStruct3InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private NestedStruct3InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private INestedStruct3InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private INestedStruct3Interface backendServiceMock = mock(INestedStruct3Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private INestedStruct3InterfaceServiceFactory serviceFactory = mock(INestedStruct3InterfaceServiceFactory.class); + private INestedStruct3InterfaceMessageGetter clientMessagesStorage = mock(INestedStruct3InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, NestedStruct3InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(INestedStruct3InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + NestedStruct1 initprop1 = new NestedStruct1(); + //TODO fill fields + when(backendServiceMock.getProp1()).thenReturn(initprop1); + NestedStruct2 initprop2 = new NestedStruct2(); + //TODO fill fields + when(backendServiceMock.getProp2()).thenReturn(initprop2); + NestedStruct3 initprop3 = new NestedStruct3(); + //TODO fill fields + when(backendServiceMock.getProp3()).thenReturn(initprop3); + + + Message registerMsg = Message.obtain(null, NestedStruct3InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp1(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp2(); + inOrderBackendService.verify(backendServiceMock, times(1)).getProp3(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + + NestedStruct3 receivedprop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + // assertEquals(receivedprop1, initprop1); + // assertEquals(receivedprop2, initprop2); + // assertEquals(receivedprop3, initprop3); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, NestedStruct3InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(NestedStruct3InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(INestedStruct3InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop1PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.PROP_Prop1.getValue()); + Bundle data = new Bundle(); + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("prop1", new NestedStruct1Parcelable(testprop1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp1( any(NestedStruct1.class)); + + } + + @Test + public void whenNotifiedprop1() + { + NestedStruct1 testprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedAdapterAsEventListener.onProp1Changed(testprop1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.SET_Prop1.getValue(), response.what); + Bundle data = response.getData(); + + + NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedprop1, testprop1); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop2PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.PROP_Prop2.getValue()); + Bundle data = new Bundle(); + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("prop2", new NestedStruct2Parcelable(testprop2)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp2( any(NestedStruct2.class)); + + } + + @Test + public void whenNotifiedprop2() + { + NestedStruct2 testprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + + testedAdapterAsEventListener.onProp2Changed(testprop2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.SET_Prop2.getValue(), response.what); + Bundle data = response.getData(); + + + NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); + + assertEquals(receivedprop2, testprop2); + } +//TODO do not add when a property is readonly + @Test + public void onReceiveprop3PropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.PROP_Prop3.getValue()); + Bundle data = new Bundle(); + NestedStruct3 testprop3 = Testbed2TestHelper.makeTestNestedStruct3(); + data.putParcelable("prop3", new NestedStruct3Parcelable(testprop3)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setProp3( any(NestedStruct3.class)); + + } + + @Test + public void whenNotifiedprop3() + { + NestedStruct3 testprop3 = Testbed2TestHelper.makeTestNestedStruct3(); + + testedAdapterAsEventListener.onProp3Changed(testprop3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.SET_Prop3.getValue(), response.what); + Bundle data = response.getData(); + + + NestedStruct3 receivedprop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); + + assertEquals(receivedprop3, testprop3); + } + @Test + public void whenNotifiedsig1() + { + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + + testedAdapterAsEventListener.onSig1(testparam1); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.SIG_Sig1.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); +} + @Test + public void whenNotifiedsig2() + { + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + + testedAdapterAsEventListener.onSig2(testparam1, testparam2); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.SIG_Sig2.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + + NestedStruct2 receivedparam2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedparam2, testparam2); +} + @Test + public void whenNotifiedsig3() + { + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + NestedStruct3 testparam3 = Testbed2TestHelper.makeTestNestedStruct3(); + + testedAdapterAsEventListener.onSig3(testparam1, testparam2, testparam3); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.SIG_Sig3.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + + NestedStruct2 receivedparam2 = data.getParcelable("param2", NestedStruct2Parcelable.class).getNestedStruct2(); + assertEquals(receivedparam2, testparam2); + + NestedStruct3 receivedparam3 = data.getParcelable("param3", NestedStruct3Parcelable.class).getNestedStruct3(); + assertEquals(receivedparam3, testparam3); +} + + + public void onfunc1Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.RPC_Func1Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.func1( any(NestedStruct1.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func1( any(NestedStruct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.RPC_Func1Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc2Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.RPC_Func2Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("param2", new NestedStruct2Parcelable(testparam2)); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.func2( any(NestedStruct1.class), any(NestedStruct2.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func2( any(NestedStruct1.class), any(NestedStruct2.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.RPC_Func2Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfunc3Request() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct3InterfaceMessageType.RPC_Func3Req.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + NestedStruct2 testparam2 = Testbed2TestHelper.makeTestNestedStruct2(); + data.putParcelable("param2", new NestedStruct2Parcelable(testparam2)); + NestedStruct3 testparam3 = Testbed2TestHelper.makeTestNestedStruct3(); + data.putParcelable("param3", new NestedStruct3Parcelable(testparam3)); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.func3( any(NestedStruct1.class), any(NestedStruct2.class), any(NestedStruct3.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).func3( any(NestedStruct1.class), any(NestedStruct2.class), any(NestedStruct3.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct3InterfaceMessageType.RPC_Func3Resp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed2/testbed2_api/additions.gradle b/goldenmaster/testbed2/testbed2_api/additions.gradle new file mode 100644 index 0000000..999f2c2 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'testbed2.testbed2_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/testbed2/testbed2_api/build.gradle b/goldenmaster/testbed2/testbed2_api/build.gradle new file mode 100644 index 0000000..d7745c0 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "testbed2" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java new file mode 100644 index 0000000..ac66073 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java @@ -0,0 +1,91 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +//TODO imported/extern modules +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractManyParamInterface implements IManyParamInterface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IManyParamInterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IManyParamInterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(int newValue) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(int newValue) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireProp3Changed(int newValue) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onProp3Changed(newValue); + } + } + + @Override + public void fireProp4Changed(int newValue) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onProp4Changed(newValue); + } + } + + @Override + public void fireSig1(int param1) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(int param1, int param2) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + @Override + public void fireSig3(int param1, int param2, int param3) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onSig3(param1, param2, param3); + } + } + + @Override + public void fireSig4(int param1, int param2, int param3, int param4) { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.onSig4(param1, param2, param3, param4); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IManyParamInterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java new file mode 100644 index 0000000..8051ed8 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java @@ -0,0 +1,49 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct1Interface; +//TODO imported/extern modules +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNestedStruct1Interface implements INestedStruct1Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INestedStruct1InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INestedStruct1InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(NestedStruct1 newValue) { + for (INestedStruct1InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireSig1(NestedStruct1 param1) { + for (INestedStruct1InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INestedStruct1InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java new file mode 100644 index 0000000..05e271d --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java @@ -0,0 +1,63 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct2Interface; +//TODO imported/extern modules +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNestedStruct2Interface implements INestedStruct2Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INestedStruct2InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INestedStruct2InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(NestedStruct1 newValue) { + for (INestedStruct2InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(NestedStruct2 newValue) { + for (INestedStruct2InterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireSig1(NestedStruct1 param1) { + for (INestedStruct2InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(NestedStruct1 param1, NestedStruct2 param2) { + for (INestedStruct2InterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INestedStruct2InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java new file mode 100644 index 0000000..7339b4a --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java @@ -0,0 +1,77 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct3Interface; +//TODO imported/extern modules +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractNestedStruct3Interface implements INestedStruct3Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(INestedStruct3InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(INestedStruct3InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireProp1Changed(NestedStruct1 newValue) { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.onProp1Changed(newValue); + } + } + + @Override + public void fireProp2Changed(NestedStruct2 newValue) { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.onProp2Changed(newValue); + } + } + + @Override + public void fireProp3Changed(NestedStruct3 newValue) { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.onProp3Changed(newValue); + } + } + + @Override + public void fireSig1(NestedStruct1 param1) { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.onSig1(param1); + } + } + + @Override + public void fireSig2(NestedStruct1 param1, NestedStruct2 param2) { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.onSig2(param1, param2); + } + } + + @Override + public void fireSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.onSig3(param1, param2, param3); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (INestedStruct3InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum1.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum1.java new file mode 100644 index 0000000..f0123ab --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum1.java @@ -0,0 +1,32 @@ +package testbed2.testbed2_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum1 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2), + @JsonProperty("3") + Value3(3), + @JsonProperty("4") + Value4(4); + + private final int value; + + Enum1(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum1 fromValue(int value) { + for (Enum1 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum2.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum2.java new file mode 100644 index 0000000..c8678d3 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum2.java @@ -0,0 +1,32 @@ +package testbed2.testbed2_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum2 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2), + @JsonProperty("3") + Value3(3), + @JsonProperty("4") + Value4(4); + + private final int value; + + Enum2(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum2 fromValue(int value) { + for (Enum2 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum3.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum3.java new file mode 100644 index 0000000..28bf045 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Enum3.java @@ -0,0 +1,32 @@ +package testbed2.testbed2_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum3 +{ + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2), + @JsonProperty("3") + Value3(3), + @JsonProperty("4") + Value4(4); + + private final int value; + + Enum3(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum3 fromValue(int value) { + for (Enum3 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterface.java new file mode 100644 index 0000000..579026d --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterface.java @@ -0,0 +1,54 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.concurrent.CompletableFuture; + + + public interface IManyParamInterface { + // properties + void setProp1(int prop1); + int getProp1(); + void fireProp1Changed(int newValue); + + void setProp2(int prop2); + int getProp2(); + void fireProp2Changed(int newValue); + + void setProp3(int prop3); + int getProp3(); + void fireProp3Changed(int newValue); + + void setProp4(int prop4); + int getProp4(); + void fireProp4Changed(int newValue); + + // methods + int func1(int param1); + CompletableFuture func1Async(int param1); + int func2(int param1, int param2); + CompletableFuture func2Async(int param1, int param2); + int func3(int param1, int param2, int param3); + CompletableFuture func3Async(int param1, int param2, int param3); + int func4(int param1, int param2, int param3, int param4); + CompletableFuture func4Async(int param1, int param2, int param3, int param4); + public void fireSig1(int param1); + public void fireSig2(int param1, int param2); + public void fireSig3(int param1, int param2, int param3); + public void fireSig4(int param1, int param2, int param3, int param4); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IManyParamInterfaceEventListener listener); + void removeEventListener(IManyParamInterfaceEventListener listener); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterfaceEventListener.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterfaceEventListener.java new file mode 100644 index 0000000..f5c49e5 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/IManyParamInterfaceEventListener.java @@ -0,0 +1,23 @@ +package testbed2.testbed2_api; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + public interface IManyParamInterfaceEventListener { + void onProp1Changed(int newValue); + void onProp2Changed(int newValue); + void onProp3Changed(int newValue); + void onProp4Changed(int newValue); + void onSig1(int param1); + void onSig2(int param1, int param2); + void onSig3(int param1, int param2, int param3); + void onSig4(int param1, int param2, int param3, int param4); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java new file mode 100644 index 0000000..6f7e76c --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java @@ -0,0 +1,33 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.concurrent.CompletableFuture; + + + public interface INestedStruct1Interface { + // properties + void setProp1(NestedStruct1 prop1); + NestedStruct1 getProp1(); + void fireProp1Changed(NestedStruct1 newValue); + + // methods + NestedStruct1 func1(NestedStruct1 param1); + CompletableFuture func1Async(NestedStruct1 param1); + public void fireSig1(NestedStruct1 param1); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INestedStruct1InterfaceEventListener listener); + void removeEventListener(INestedStruct1InterfaceEventListener listener); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1InterfaceEventListener.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1InterfaceEventListener.java new file mode 100644 index 0000000..c031d4e --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1InterfaceEventListener.java @@ -0,0 +1,17 @@ +package testbed2.testbed2_api; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + public interface INestedStruct1InterfaceEventListener { + void onProp1Changed(NestedStruct1 newValue); + void onSig1(NestedStruct1 param1); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2Interface.java new file mode 100644 index 0000000..77e9aa9 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2Interface.java @@ -0,0 +1,40 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.concurrent.CompletableFuture; + + + public interface INestedStruct2Interface { + // properties + void setProp1(NestedStruct1 prop1); + NestedStruct1 getProp1(); + void fireProp1Changed(NestedStruct1 newValue); + + void setProp2(NestedStruct2 prop2); + NestedStruct2 getProp2(); + void fireProp2Changed(NestedStruct2 newValue); + + // methods + NestedStruct1 func1(NestedStruct1 param1); + CompletableFuture func1Async(NestedStruct1 param1); + NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2); + CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2); + public void fireSig1(NestedStruct1 param1); + public void fireSig2(NestedStruct1 param1, NestedStruct2 param2); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INestedStruct2InterfaceEventListener listener); + void removeEventListener(INestedStruct2InterfaceEventListener listener); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2InterfaceEventListener.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2InterfaceEventListener.java new file mode 100644 index 0000000..a57a04c --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct2InterfaceEventListener.java @@ -0,0 +1,19 @@ +package testbed2.testbed2_api; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + public interface INestedStruct2InterfaceEventListener { + void onProp1Changed(NestedStruct1 newValue); + void onProp2Changed(NestedStruct2 newValue); + void onSig1(NestedStruct1 param1); + void onSig2(NestedStruct1 param1, NestedStruct2 param2); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3Interface.java new file mode 100644 index 0000000..b1c1f61 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3Interface.java @@ -0,0 +1,47 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + +import java.util.concurrent.CompletableFuture; + + + public interface INestedStruct3Interface { + // properties + void setProp1(NestedStruct1 prop1); + NestedStruct1 getProp1(); + void fireProp1Changed(NestedStruct1 newValue); + + void setProp2(NestedStruct2 prop2); + NestedStruct2 getProp2(); + void fireProp2Changed(NestedStruct2 newValue); + + void setProp3(NestedStruct3 prop3); + NestedStruct3 getProp3(); + void fireProp3Changed(NestedStruct3 newValue); + + // methods + NestedStruct1 func1(NestedStruct1 param1); + CompletableFuture func1Async(NestedStruct1 param1); + NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2); + CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2); + NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); + CompletableFuture func3Async(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); + public void fireSig1(NestedStruct1 param1); + public void fireSig2(NestedStruct1 param1, NestedStruct2 param2); + public void fireSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(INestedStruct3InterfaceEventListener listener); + void removeEventListener(INestedStruct3InterfaceEventListener listener); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3InterfaceEventListener.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3InterfaceEventListener.java new file mode 100644 index 0000000..91d7ec4 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct3InterfaceEventListener.java @@ -0,0 +1,21 @@ +package testbed2.testbed2_api; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + public interface INestedStruct3InterfaceEventListener { + void onProp1Changed(NestedStruct1 newValue); + void onProp2Changed(NestedStruct2 newValue); + void onProp3Changed(NestedStruct3 newValue); + void onSig1(NestedStruct1 param1); + void onSig2(NestedStruct1 param1, NestedStruct2 param2); + void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct1.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct1.java new file mode 100644 index 0000000..8eb5c19 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct1.java @@ -0,0 +1,44 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class NestedStruct1 { + + public NestedStruct1(Struct1 field1) + { + this.field1 = field1; + } + + public NestedStruct1() + { + this.field1 = new Struct1(); + } + @JsonProperty("field1") + public Struct1 field1; + + public NestedStruct1(NestedStruct1 other) + { + this.field1 = new Struct1(other.field1); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NestedStruct1)) return false; + NestedStruct1 other = (NestedStruct1) o; + + return + Objects.equals(this.field1, other.field1); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Objects.hashCode(field1); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct2.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct2.java new file mode 100644 index 0000000..376adfd --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct2.java @@ -0,0 +1,51 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class NestedStruct2 { + + public NestedStruct2(Struct1 field1, Struct2 field2) + { + this.field1 = field1; + this.field2 = field2; + } + + public NestedStruct2() + { + this.field1 = new Struct1(); + this.field2 = new Struct2(); + } + @JsonProperty("field1") + public Struct1 field1; + @JsonProperty("field2") + public Struct2 field2; + + public NestedStruct2(NestedStruct2 other) + { + this.field1 = new Struct1(other.field1); + this.field2 = new Struct2(other.field2); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NestedStruct2)) return false; + NestedStruct2 other = (NestedStruct2) o; + + return + Objects.equals(this.field1, other.field1) + && Objects.equals(this.field2, other.field2); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Objects.hashCode(field1); + result = 31 * result + Objects.hashCode(field2); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java new file mode 100644 index 0000000..842aa63 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java @@ -0,0 +1,87 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class NestedStruct3 { + + public NestedStruct3(Enum1 field1, Struct2 field2, Struct3 field3, Enum1[] fieldX, Struct2[] fieldY, Struct3[] fieldZ) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + this.fieldX = fieldX; + this.fieldY = fieldY; + this.fieldZ = fieldZ; + } + + public NestedStruct3() + { + this.field1 = Enum1.values()[0]; + this.field2 = new Struct2(); + this.field3 = new Struct3(); + this.fieldX = new Enum1[0]; + this.fieldY = new Struct2[0]; + this.fieldZ = new Struct3[0]; + } + @JsonProperty("field1") + public Enum1 field1; + @JsonProperty("field2") + public Struct2 field2; + @JsonProperty("field3") + public Struct3 field3; + @JsonProperty("field_x") + public Enum1[] fieldX; + @JsonProperty("field_y") + public Struct2[] fieldY; + @JsonProperty("field_z") + public Struct3[] fieldZ; + + public NestedStruct3(NestedStruct3 other) + { + this.field1 = other.field1; + this.field2 = new Struct2(other.field2); + this.field3 = new Struct3(other.field3); + this.fieldX = java.util.Arrays.copyOf(other.fieldX, other.fieldX.length); + this.fieldY = new Struct2[other.fieldY.length]; + for (int i = 0; i < other.fieldY.length; i++) + { + this.fieldY[i] = new Struct2(other.fieldY[i]); + } + this.fieldZ = new Struct3[other.fieldZ.length]; + for (int i = 0; i < other.fieldZ.length; i++) + { + this.fieldZ[i] = new Struct3(other.fieldZ[i]); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NestedStruct3)) return false; + NestedStruct3 other = (NestedStruct3) o; + + return + this.field1 == other.field1 + && Objects.equals(this.field2, other.field2) + && Objects.equals(this.field3, other.field3) + && Arrays.equals(this.fieldX, other.fieldX) + && Arrays.equals(this.fieldY, other.fieldY) + && Arrays.equals(this.fieldZ, other.fieldZ); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Objects.hashCode(field1); + result = 31 * result + Objects.hashCode(field2); + result = 31 * result + Objects.hashCode(field3); + result = 31 * result + Arrays.hashCode(fieldX); + result = 31 * result + Arrays.hashCode(fieldY); + result = 31 * result + Arrays.hashCode(fieldZ); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct1.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct1.java new file mode 100644 index 0000000..f7d812f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct1.java @@ -0,0 +1,43 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct1 { + + public Struct1(int field1) + { + this.field1 = field1; + } + + public Struct1() + { + } + @JsonProperty("field1") + public int field1; + + public Struct1(Struct1 other) + { + this.field1 = other.field1; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct1)) return false; + Struct1 other = (Struct1) o; + + return + this.field1 == other.field1; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct2.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct2.java new file mode 100644 index 0000000..47d4f1f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct2.java @@ -0,0 +1,49 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct2 { + + public Struct2(int field1, int field2) + { + this.field1 = field1; + this.field2 = field2; + } + + public Struct2() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + + public Struct2(Struct2 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct2)) return false; + Struct2 other = (Struct2) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct3.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct3.java new file mode 100644 index 0000000..8b240a1 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct3.java @@ -0,0 +1,55 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct3 { + + public Struct3(int field1, int field2, int field3) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public Struct3() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + @JsonProperty("field3") + public int field3; + + public Struct3(Struct3 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + this.field3 = other.field3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct3)) return false; + Struct3 other = (Struct3) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2 + && this.field3 == other.field3; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + result = 31 * result + Integer.hashCode(field3); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct4.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct4.java new file mode 100644 index 0000000..c9a5e8c --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Struct4.java @@ -0,0 +1,61 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Struct4 { + + public Struct4(int field1, int field2, int field3, int field4) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + this.field4 = field4; + } + + public Struct4() + { + } + @JsonProperty("field1") + public int field1; + @JsonProperty("field2") + public int field2; + @JsonProperty("field3") + public int field3; + @JsonProperty("field4") + public int field4; + + public Struct4(Struct4 other) + { + this.field1 = other.field1; + this.field2 = other.field2; + this.field3 = other.field3; + this.field4 = other.field4; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Struct4)) return false; + Struct4 other = (Struct4) o; + + return + this.field1 == other.field1 + && this.field2 == other.field2 + && this.field3 == other.field3 + && this.field4 == other.field4; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Integer.hashCode(field1); + result = 31 * result + Integer.hashCode(field2); + result = 31 * result + Integer.hashCode(field3); + result = 31 * result + Integer.hashCode(field4); + return result; + } + + +} diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java new file mode 100644 index 0000000..f62ad6e --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java @@ -0,0 +1,75 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + + +public class Testbed2TestHelper +{ + + static public Struct1 makeTestStruct1() + { + Struct1 testStruct = new Struct1(); + testStruct.field1 = 1; + return testStruct; + } + + static public Struct2 makeTestStruct2() + { + Struct2 testStruct = new Struct2(); + testStruct.field1 = 1; + testStruct.field2 = 1; + return testStruct; + } + + static public Struct3 makeTestStruct3() + { + Struct3 testStruct = new Struct3(); + testStruct.field1 = 1; + testStruct.field2 = 1; + testStruct.field3 = 1; + return testStruct; + } + + static public Struct4 makeTestStruct4() + { + Struct4 testStruct = new Struct4(); + testStruct.field1 = 1; + testStruct.field2 = 1; + testStruct.field3 = 1; + testStruct.field4 = 1; + return testStruct; + } + + static public NestedStruct1 makeTestNestedStruct1() + { + NestedStruct1 testStruct = new NestedStruct1(); + testStruct.field1 = makeTestStruct1(); + return testStruct; + } + + static public NestedStruct2 makeTestNestedStruct2() + { + NestedStruct2 testStruct = new NestedStruct2(); + testStruct.field1 = makeTestStruct1(); + testStruct.field2 = makeTestStruct2(); + return testStruct; + } + + static public NestedStruct3 makeTestNestedStruct3() + { + NestedStruct3 testStruct = new NestedStruct3(); + testStruct.field1 = Enum1.Value2; + testStruct.field2 = makeTestStruct2(); + testStruct.field3 = makeTestStruct3(); + testStruct.fieldX = new Enum1[1]; + testStruct.fieldX[0] = Enum1.Value2; + testStruct.fieldY = new Struct2[1]; + testStruct.fieldY[0] = makeTestStruct2(); + testStruct.fieldZ = new Struct3[1]; + testStruct.fieldZ[0] = makeTestStruct3(); + return testStruct; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/build.gradle b/goldenmaster/testbed2/testbed2_client_example/build.gradle new file mode 100644 index 0000000..2eeada1 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/build.gradle @@ -0,0 +1,41 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'testbed2.testbed2_client_example' + compileSdk 35 + + defaultConfig { + applicationId "testbed2.testbed2_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':testbed2_api') + implementation project(':testbed2_android_messenger') + implementation project(':testbed2_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/AndroidManifest.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..77169de --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java new file mode 100644 index 0000000..5bd796d --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java @@ -0,0 +1,376 @@ +package testbed2.testbed2_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import testbed2.testbed2_android_client.ManyParamInterfaceClient; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class Testbed2TestClientApp extends Activity implements IManyParamInterfaceEventListener +{ + + private static final String TAG = "Testbed2TestClientApp"; + + private ManyParamInterfaceClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "testbed2.testbed2serviceexample.Testbed2TestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + int newProp1 = mClient.getProp1(); + + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mClient.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + Button bProp2 = new Button(this); + bProp2.setText("Set prop2"); + bProp2.setBackgroundColor(Color.GREEN); + + bProp2.setOnClickListener(v -> { + int newProp2 = mClient.getProp2(); + + //TODO increment + Log.i(TAG, "SET prop2" + newProp2); + mClient.setProp2(newProp2); + }); + propertyButtonsLine.addView(bProp2); + Button bProp3 = new Button(this); + bProp3.setText("Set prop3"); + bProp3.setBackgroundColor(Color.GREEN); + + bProp3.setOnClickListener(v -> { + int newProp3 = mClient.getProp3(); + + //TODO increment + Log.i(TAG, "SET prop3" + newProp3); + mClient.setProp3(newProp3); + }); + propertyButtonsLine.addView(bProp3); + Button bProp4 = new Button(this); + bProp4.setText("Set prop4"); + bProp4.setBackgroundColor(Color.GREEN); + + bProp4.setOnClickListener(v -> { + int newProp4 = mClient.getProp4(); + + //TODO increment + Log.i(TAG, "SET prop4" + newProp4); + mClient.setProp4(newProp4); + }); + propertyButtonsLine.addView(bProp4); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bFunc1 = new Button(this); + bFunc1.setText("func1"); + + bFunc1.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func1 "); + int param1 = 1; + CompletableFuture method_res + = mClient.func1Async(param1).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func1 result "+ i); + return i; + }); + }); + bFunc1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc1); + Button bFunc2 = new Button(this); + bFunc2.setText("func2"); + + bFunc2.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func2 "); + int param1 = 1; + int param2 = 1; + CompletableFuture method_res + = mClient.func2Async(param1, param2).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func2 result "+ i); + return i; + }); + }); + bFunc2.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc2); + Button bFunc3 = new Button(this); + bFunc3.setText("func3"); + + bFunc3.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func3 "); + int param1 = 1; + int param2 = 1; + int param3 = 1; + CompletableFuture method_res + = mClient.func3Async(param1, param2, param3).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func3 result "+ i); + return i; + }); + }); + bFunc3.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc3); + Button bFunc4 = new Button(this); + bFunc4.setText("func4"); + + bFunc4.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD func4 "); + int param1 = 1; + int param2 = 1; + int param3 = 1; + int param4 = 1; + CompletableFuture method_res + = mClient.func4Async(param1, param2, param3, param4).thenApply( + i -> { + outputTextVieMethodRes.setText("Got func4 result "+ i); + return i; + }); + }); + bFunc4.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bFunc4); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new ManyParamInterfaceClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onProp1Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onProp2Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop2 " + newValue); + Log.w(TAG, "Property from service: prop2 " + newValue); + } + @Override + public void onProp3Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop3 " + newValue); + Log.w(TAG, "Property from service: prop3 " + newValue); + } + @Override + public void onProp4Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop4 " + newValue); + Log.w(TAG, "Property from service: prop4 " + newValue); + } + @Override + public void onSig1(int param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig2(int param1, int param2) + { + String text = "Signal sig2 "+ " " + param1+ " " + param2; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig3(int param1, int param2, int param3) + { + String text = "Signal sig3 "+ " " + param1+ " " + param2+ " " + param3; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig4(int param1, int param2, int param3, int param4) + { + String text = "Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/values-night/themes.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/colors.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/strings.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..cf387eb --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Testbed2TestClientApp + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/themes.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_impl/additions.gradle b/goldenmaster/testbed2/testbed2_impl/additions.gradle new file mode 100644 index 0000000..1f4ebbd --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'testbed2.testbed2_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_impl/build.gradle b/goldenmaster/testbed2/testbed2_impl/build.gradle new file mode 100644 index 0000000..8aa2726 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "testbed2" +version = "1.0.0" + +android { + namespace 'testbed2.testbed2_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':testbed2_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java new file mode 100644 index 0000000..177ce59 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java @@ -0,0 +1,227 @@ +package testbed2.testbed2_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class ManyParamInterfaceService extends AbstractManyParamInterface { + + private final static String TAG = "ManyParamInterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private int m_prop1 = 0; + private int m_prop2 = 0; + private int m_prop3 = 0; + private int m_prop4 = 0; + + public ManyParamInterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(int prop1) + { + Log.i(TAG, "request setProp1 called "); + if (m_prop1 != prop1) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public int getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(int prop2) + { + Log.i(TAG, "request setProp2 called "); + if (m_prop2 != prop2) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public int getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + @Override + public void setProp3(int prop3) + { + Log.i(TAG, "request setProp3 called "); + if (m_prop3 != prop3) + { + m_prop3 = prop3; + onProp3Changed(m_prop3); + } + + } + + @Override + public int getProp3() + { + Log.i(TAG, "request getProp3 called,"); + return m_prop3; + } + + + @Override + public void setProp4(int prop4) + { + Log.i(TAG, "request setProp4 called "); + if (m_prop4 != prop4) + { + m_prop4 = prop4; + onProp4Changed(m_prop4); + } + + } + + @Override + public int getProp4() + { + Log.i(TAG, "request getProp4 called,"); + return m_prop4; + } + + + // methods + + @Override + public int func1(int param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return 0; + } + + @Override + public CompletableFuture func1Async(int param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public int func2(int param1, int param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return 0; + } + + @Override + public CompletableFuture func2Async(int param1, int param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public int func3(int param1, int param2, int param3) { + Log.w(TAG, "request method func3 called, returnig default"); + return 0; + } + + @Override + public CompletableFuture func3Async(int param1, int param2, int param3) { + return CompletableFuture.supplyAsync( + () -> {return func3(param1, param2, param3); }, + executor); + } + + @Override + public int func4(int param1, int param2, int param3, int param4) { + Log.w(TAG, "request method func4 called, returnig default"); + return 0; + } + + @Override + public CompletableFuture func4Async(int param1, int param2, int param3, int param4) { + return CompletableFuture.supplyAsync( + () -> {return func4(param1, param2, param3, param4); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(int newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(int newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + private void onProp3Changed(int newValue) + { + Log.i(TAG, "onProp3Changed, will pass notification to all listeners"); + fireProp3Changed(newValue); + } + private void onProp4Changed(int newValue) + { + Log.i(TAG, "onProp4Changed, will pass notification to all listeners"); + fireProp4Changed(newValue); + } + public void onSig1(int param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(int param1, int param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + public void onSig3(int param1, int param2, int param3) + { + Log.i(TAG, "onSig3, will pass notification to all listeners"); + fireSig3(param1, param2, param3); + } + public void onSig4(int param1, int param2, int param3, int param4) + { + Log.i(TAG, "onSig4, will pass notification to all listeners"); + fireSig4(param1, param2, param3, param4); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java new file mode 100644 index 0000000..c38396a --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java @@ -0,0 +1,95 @@ +package testbed2.testbed2_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NestedStruct1InterfaceService extends AbstractNestedStruct1Interface { + + private final static String TAG = "NestedStruct1InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private NestedStruct1 m_prop1 = new NestedStruct1(); + + public NestedStruct1InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + // methods + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java new file mode 100644 index 0000000..c966dd1 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java @@ -0,0 +1,139 @@ +package testbed2.testbed2_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NestedStruct2InterfaceService extends AbstractNestedStruct2Interface { + + private final static String TAG = "NestedStruct2InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private NestedStruct1 m_prop1 = new NestedStruct1(); + private NestedStruct2 m_prop2 = new NestedStruct2(); + + public NestedStruct2InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + // methods + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(NestedStruct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java new file mode 100644 index 0000000..055bfd0 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java @@ -0,0 +1,183 @@ +package testbed2.testbed2_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class NestedStruct3InterfaceService extends AbstractNestedStruct3Interface { + + private final static String TAG = "NestedStruct3InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private NestedStruct1 m_prop1 = new NestedStruct1(); + private NestedStruct2 m_prop2 = new NestedStruct2(); + private NestedStruct3 m_prop3 = new NestedStruct3(); + + public NestedStruct3InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called "); + if (! m_prop1.equals(prop1)) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "request setProp2 called "); + if (! m_prop2.equals(prop2)) + { + m_prop2 = prop2; + onProp2Changed(m_prop2); + } + + } + + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "request getProp2 called,"); + return m_prop2; + } + + + @Override + public void setProp3(NestedStruct3 prop3) + { + Log.i(TAG, "request setProp3 called "); + if (! m_prop3.equals(prop3)) + { + m_prop3 = prop3; + onProp3Changed(m_prop3); + } + + } + + @Override + public NestedStruct3 getProp3() + { + Log.i(TAG, "request getProp3 called,"); + return m_prop3; + } + + + // methods + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + Log.w(TAG, "request method func1 called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { + Log.w(TAG, "request method func2 called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + Log.w(TAG, "request method func3 called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture func3Async(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + return CompletableFuture.supplyAsync( + () -> {return func3(param1, param2, param3); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + private void onProp2Changed(NestedStruct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + private void onProp3Changed(NestedStruct3 newValue) + { + Log.i(TAG, "onProp3Changed, will pass notification to all listeners"); + fireProp3Changed(newValue); + } + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + public void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + { + Log.i(TAG, "onSig3, will pass notification to all listeners"); + fireSig3(param1, param2, param3); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java new file mode 100644 index 0000000..0b78410 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java @@ -0,0 +1,262 @@ +package testbed2.testbed2jniclient; + +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_api.IManyParamInterfaceEventListener; + +import testbed2.testbed2_android_client.ManyParamInterfaceClient; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class ManyParamInterfaceJniClient extends AbstractManyParamInterface implements IManyParamInterfaceEventListener +{ + + private static final String TAG = "ManyParamInterfaceJniClient"; + + private ManyParamInterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed2.testbed2jniservice.ManyParamInterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(int prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public int getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(int prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public int getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + @Override + public void setProp3(int prop3) + { + Log.i(TAG, "got request from ue, setProp3" + (prop3)); + mMessengerClient.setProp3(prop3); + } + @Override + public int getProp3() + { + Log.i(TAG, "got request from ue, getProp3"); + return mMessengerClient.getProp3(); + } + + @Override + public void setProp4(int prop4) + { + Log.i(TAG, "got request from ue, setProp4" + (prop4)); + mMessengerClient.setProp4(prop4); + } + @Override + public int getProp4() + { + Log.i(TAG, "got request from ue, getProp4"); + return mMessengerClient.getProp4(); + } + + public int func1(int param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, int param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, int param1) + public CompletableFuture func1Async(int param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public int func2(int param1, int param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, int param1, int param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, int param1, int param2) + public CompletableFuture func2Async(int param1, int param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + public int func3(int param1, int param2, int param3) + { + Log.v(TAG, "Blocking callfunc3 - should not be used "); + return mMessengerClient.func3(param1, param2, param3); + } + + public void func3Async(String callId, int param1, int param2, int param3){ + Log.v(TAG, "non blocking call func3 "); + mMessengerClient.func3Async(param1, param2, param3).thenAccept(i -> { + nativeOnFunc3Result(i, callId);}); + } + + //Should not be called directly, use func3Async(String callId, int param1, int param2, int param3) + public CompletableFuture func3Async(int param1, int param2, int param3) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func3Async(param1, param2, param3); + } + public int func4(int param1, int param2, int param3, int param4) + { + Log.v(TAG, "Blocking callfunc4 - should not be used "); + return mMessengerClient.func4(param1, param2, param3, param4); + } + + public void func4Async(String callId, int param1, int param2, int param3, int param4){ + Log.v(TAG, "non blocking call func4 "); + mMessengerClient.func4Async(param1, param2, param3, param4).thenAccept(i -> { + nativeOnFunc4Result(i, callId);}); + } + + //Should not be called directly, use func4Async(String callId, int param1, int param2, int param3, int param4) + public CompletableFuture func4Async(int param1, int param2, int param3, int param4) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func4Async(param1, param2, param3, param4); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new ManyParamInterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onProp3Changed(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp3Changed(newValue); + } + @Override + public void onProp4Changed(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp4Changed(newValue); + } + @Override + public void onSig1(int param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(int param1, int param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + @Override + public void onSig3(int param1, int param2, int param3) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param1+ " " + param2+ " " + param3); + nativeOnSig3(param1, param2, param3); + } + @Override + public void onSig4(int param1, int param2, int param3, int param4) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4); + nativeOnSig4(param1, param2, param3, param4); + } + private native void nativeOnProp1Changed(int prop1); + private native void nativeOnProp2Changed(int prop2); + private native void nativeOnProp3Changed(int prop3); + private native void nativeOnProp4Changed(int prop4); + private native void nativeOnSig1(int param1); + private native void nativeOnSig2(int param1, int param2); + private native void nativeOnSig3(int param1, int param2, int param3); + private native void nativeOnSig4(int param1, int param2, int param3, int param4); + private native void nativeOnFunc1Result(int result, String callId); + private native void nativeOnFunc2Result(int result, String callId); + private native void nativeOnFunc3Result(int result, String callId); + private native void nativeOnFunc4Result(int result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java new file mode 100644 index 0000000..7291643 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -0,0 +1,124 @@ +package testbed2.testbed2jniclient; + +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; + +import testbed2.testbed2_android_client.NestedStruct1InterfaceClient; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NestedStruct1InterfaceJniClient extends AbstractNestedStruct1Interface implements INestedStruct1InterfaceEventListener +{ + + private static final String TAG = "NestedStruct1InterfaceJniClient"; + + private NestedStruct1InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed2.testbed2jniservice.NestedStruct1InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + public NestedStruct1 func1(NestedStruct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, NestedStruct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, NestedStruct1 param1) + public CompletableFuture func1Async(NestedStruct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NestedStruct1InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(NestedStruct1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(NestedStruct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + private native void nativeOnProp1Changed(NestedStruct1 prop1); + private native void nativeOnSig1(NestedStruct1 param1); + private native void nativeOnFunc1Result(NestedStruct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java new file mode 100644 index 0000000..5147d73 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java @@ -0,0 +1,170 @@ +package testbed2.testbed2jniclient; + +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; + +import testbed2.testbed2_android_client.NestedStruct2InterfaceClient; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NestedStruct2InterfaceJniClient extends AbstractNestedStruct2Interface implements INestedStruct2InterfaceEventListener +{ + + private static final String TAG = "NestedStruct2InterfaceJniClient"; + + private NestedStruct2InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed2.testbed2jniservice.NestedStruct2InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + public NestedStruct1 func1(NestedStruct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, NestedStruct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, NestedStruct1 param1) + public CompletableFuture func1Async(NestedStruct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, NestedStruct1 param1, NestedStruct2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, NestedStruct1 param1, NestedStruct2 param2) + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NestedStruct2InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(NestedStruct1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(NestedStruct2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(NestedStruct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + private native void nativeOnProp1Changed(NestedStruct1 prop1); + private native void nativeOnProp2Changed(NestedStruct2 prop2); + private native void nativeOnSig1(NestedStruct1 param1); + private native void nativeOnSig2(NestedStruct1 param1, NestedStruct2 param2); + private native void nativeOnFunc1Result(NestedStruct1 result, String callId); + private native void nativeOnFunc2Result(NestedStruct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java new file mode 100644 index 0000000..0de91cc --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java @@ -0,0 +1,216 @@ +package testbed2.testbed2jniclient; + +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; + +import testbed2.testbed2_android_client.NestedStruct3InterfaceClient; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class NestedStruct3InterfaceJniClient extends AbstractNestedStruct3Interface implements INestedStruct3InterfaceEventListener +{ + + private static final String TAG = "NestedStruct3InterfaceJniClient"; + + private NestedStruct3InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed2.testbed2jniservice.NestedStruct3InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "got request from ue, setProp1" + (prop1)); + mMessengerClient.setProp1(prop1); + } + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "got request from ue, getProp1"); + return mMessengerClient.getProp1(); + } + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "got request from ue, setProp2" + (prop2)); + mMessengerClient.setProp2(prop2); + } + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "got request from ue, getProp2"); + return mMessengerClient.getProp2(); + } + + @Override + public void setProp3(NestedStruct3 prop3) + { + Log.i(TAG, "got request from ue, setProp3" + (prop3)); + mMessengerClient.setProp3(prop3); + } + @Override + public NestedStruct3 getProp3() + { + Log.i(TAG, "got request from ue, getProp3"); + return mMessengerClient.getProp3(); + } + + public NestedStruct1 func1(NestedStruct1 param1) + { + Log.v(TAG, "Blocking callfunc1 - should not be used "); + return mMessengerClient.func1(param1); + } + + public void func1Async(String callId, NestedStruct1 param1){ + Log.v(TAG, "non blocking call func1 "); + mMessengerClient.func1Async(param1).thenAccept(i -> { + nativeOnFunc1Result(i, callId);}); + } + + //Should not be called directly, use func1Async(String callId, NestedStruct1 param1) + public CompletableFuture func1Async(NestedStruct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func1Async(param1); + } + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.v(TAG, "Blocking callfunc2 - should not be used "); + return mMessengerClient.func2(param1, param2); + } + + public void func2Async(String callId, NestedStruct1 param1, NestedStruct2 param2){ + Log.v(TAG, "non blocking call func2 "); + mMessengerClient.func2Async(param1, param2).thenAccept(i -> { + nativeOnFunc2Result(i, callId);}); + } + + //Should not be called directly, use func2Async(String callId, NestedStruct1 param1, NestedStruct2 param2) + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func2Async(param1, param2); + } + public NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + { + Log.v(TAG, "Blocking callfunc3 - should not be used "); + return mMessengerClient.func3(param1, param2, param3); + } + + public void func3Async(String callId, NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3){ + Log.v(TAG, "non blocking call func3 "); + mMessengerClient.func3Async(param1, param2, param3).thenAccept(i -> { + nativeOnFunc3Result(i, callId);}); + } + + //Should not be called directly, use func3Async(String callId, NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + public CompletableFuture func3Async(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.func3Async(param1, param2, param3); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + mMessengerClient.unbindFromService(); + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NestedStruct3InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(NestedStruct1 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(NestedStruct2 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onProp3Changed(NestedStruct3 newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp3Changed(newValue); + } + @Override + public void onSig1(NestedStruct1 param1) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + nativeOnSig2(param1, param2); + } + @Override + public void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param1+ " " + param2+ " " + param3); + nativeOnSig3(param1, param2, param3); + } + private native void nativeOnProp1Changed(NestedStruct1 prop1); + private native void nativeOnProp2Changed(NestedStruct2 prop2); + private native void nativeOnProp3Changed(NestedStruct3 prop3); + private native void nativeOnSig1(NestedStruct1 param1); + private native void nativeOnSig2(NestedStruct1 param1, NestedStruct2 param2); + private native void nativeOnSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); + private native void nativeOnFunc1Result(NestedStruct1 result, String callId); + private native void nativeOnFunc2Result(NestedStruct1 result, String callId); + private native void nativeOnFunc3Result(NestedStruct1 result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java new file mode 100644 index 0000000..ea89381 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java @@ -0,0 +1,227 @@ +package testbed2.testbed2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class ManyParamInterfaceJniService extends AbstractManyParamInterface { + + + private final static String TAG = "ManyParamInterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public ManyParamInterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(int prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public int getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(int prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public int getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + @Override + public void setProp3(int prop3) + { + Log.i(TAG, "request setProp3 called, will call native "); + nativeSetProp3(prop3); + } + + @Override + public int getProp3() + { + Log.i(TAG, "request getProp3 called, will call native "); + return nativeGetProp3(); + } + + + @Override + public void setProp4(int prop4) + { + Log.i(TAG, "request setProp4 called, will call native "); + nativeSetProp4(prop4); + } + + @Override + public int getProp4() + { + Log.i(TAG, "request getProp4 called, will call native "); + return nativeGetProp4(); + } + + + // methods + + @Override + public int func1(int param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(int param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public int func2(int param1, int param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(int param1, int param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public int func3(int param1, int param2, int param3) { + Log.w(TAG, "request method func3 called, will call native"); + return nativeFunc3(param1, param2, param3); + } + + @Override + public CompletableFuture func3Async(int param1, int param2, int param3) { + return CompletableFuture.supplyAsync( + () -> {return func3(param1, param2, param3); }, + executor); + } + + @Override + public int func4(int param1, int param2, int param3, int param4) { + Log.w(TAG, "request method func4 called, will call native"); + return nativeFunc4(param1, param2, param3, param4); + } + + @Override + public CompletableFuture func4Async(int param1, int param2, int param3, int param4) { + return CompletableFuture.supplyAsync( + () -> {return func4(param1, param2, param3, param4); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(int prop1); + private native int nativeGetProp1(); + + private native void nativeSetProp2(int prop2); + private native int nativeGetProp2(); + + private native void nativeSetProp3(int prop3); + private native int nativeGetProp3(); + + private native void nativeSetProp4(int prop4); + private native int nativeGetProp4(); + + // methods + private native int nativeFunc1(int param1); + private native int nativeFunc2(int param1, int param2); + private native int nativeFunc3(int param1, int param2, int param3); + private native int nativeFunc4(int param1, int param2, int param3, int param4); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(int newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(int newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onProp3Changed(int newValue) + { + Log.i(TAG, "onProp3Changed, will pass notification to all listeners"); + fireProp3Changed(newValue); + } + public void onProp4Changed(int newValue) + { + Log.i(TAG, "onProp4Changed, will pass notification to all listeners"); + fireProp4Changed(newValue); + } + public void onSig1(int param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(int param1, int param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + public void onSig3(int param1, int param2, int param3) + { + Log.i(TAG, "onSig3, will pass notification to all listeners"); + fireSig3(param1, param2, param3); + } + public void onSig4(int param1, int param2, int param3, int param4) + { + Log.i(TAG, "onSig4, will pass notification to all listeners"); + fireSig4(param1, param2, param3, param4); + } + +} diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java new file mode 100644 index 0000000..c7ee965 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2jniservice; + +import testbed2.testbed2_android_service.IManyParamInterfaceServiceFactory; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_api.AbstractManyParamInterface; +import testbed2.testbed2jniservice.ManyParamInterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton ManyParamInterfaceJniServiceFactory thread for the system. This is a thread for + * ManyParamInterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class ManyParamInterfaceJniServiceFactory extends HandlerThread implements IManyParamInterfaceServiceFactory +{ + private ManyParamInterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static ManyParamInterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: ManyParamInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractManyParamInterface getServiceInstance() + { + if (jniService == null) + { + jniService = new ManyParamInterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new ManyParamInterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final ManyParamInterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private ManyParamInterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static ManyParamInterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + ManyParamInterfaceJniServiceFactory t = new ManyParamInterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java new file mode 100644 index 0000000..0cfd277 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter; +import testbed2.testbed2jniservice.ManyParamInterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class ManyParamInterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "ManyParamInterfaceJniStarter"; + + + + public static IManyParamInterface start(Context context) { + stop(context); + androidService = new Intent(context, ManyParamInterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + ManyParamInterfaceJniServiceFactory factory = ManyParamInterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for ManyParamInterfaceJniServiceFactory"); + return ManyParamInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java new file mode 100644 index 0000000..090da32 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java @@ -0,0 +1,101 @@ +package testbed2.testbed2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NestedStruct1InterfaceJniService extends AbstractNestedStruct1Interface { + + + private final static String TAG = "NestedStruct1InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NestedStruct1InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + // methods + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(NestedStruct1 prop1); + private native NestedStruct1 nativeGetProp1(); + + // methods + private native NestedStruct1 nativeFunc1(NestedStruct1 param1); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + +} diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java new file mode 100644 index 0000000..f4882cf --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2jniservice; + +import testbed2.testbed2_android_service.INestedStruct1InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_api.AbstractNestedStruct1Interface; +import testbed2.testbed2jniservice.NestedStruct1InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NestedStruct1InterfaceJniServiceFactory thread for the system. This is a thread for + * NestedStruct1InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NestedStruct1InterfaceJniServiceFactory extends HandlerThread implements INestedStruct1InterfaceServiceFactory +{ + private NestedStruct1InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NestedStruct1InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NestedStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNestedStruct1Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new NestedStruct1InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NestedStruct1InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NestedStruct1InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private NestedStruct1InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NestedStruct1InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NestedStruct1InterfaceJniServiceFactory t = new NestedStruct1InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java new file mode 100644 index 0000000..bb5141a --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_service.NestedStruct1InterfaceServiceAdapter; +import testbed2.testbed2jniservice.NestedStruct1InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NestedStruct1InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NestedStruct1InterfaceJniStarter"; + + + + public static INestedStruct1Interface start(Context context) { + stop(context); + androidService = new Intent(context, NestedStruct1InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NestedStruct1InterfaceJniServiceFactory factory = NestedStruct1InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NestedStruct1InterfaceJniServiceFactory"); + return NestedStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java new file mode 100644 index 0000000..3911333 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java @@ -0,0 +1,143 @@ +package testbed2.testbed2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NestedStruct2InterfaceJniService extends AbstractNestedStruct2Interface { + + + private final static String TAG = "NestedStruct2InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NestedStruct2InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + // methods + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(NestedStruct1 prop1); + private native NestedStruct1 nativeGetProp1(); + + private native void nativeSetProp2(NestedStruct2 prop2); + private native NestedStruct2 nativeGetProp2(); + + // methods + private native NestedStruct1 nativeFunc1(NestedStruct1 param1); + private native NestedStruct1 nativeFunc2(NestedStruct1 param1, NestedStruct2 param2); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(NestedStruct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + +} diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java new file mode 100644 index 0000000..e9248a0 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2jniservice; + +import testbed2.testbed2_android_service.INestedStruct2InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_api.AbstractNestedStruct2Interface; +import testbed2.testbed2jniservice.NestedStruct2InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NestedStruct2InterfaceJniServiceFactory thread for the system. This is a thread for + * NestedStruct2InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NestedStruct2InterfaceJniServiceFactory extends HandlerThread implements INestedStruct2InterfaceServiceFactory +{ + private NestedStruct2InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NestedStruct2InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NestedStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNestedStruct2Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new NestedStruct2InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NestedStruct2InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NestedStruct2InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private NestedStruct2InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NestedStruct2InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NestedStruct2InterfaceJniServiceFactory t = new NestedStruct2InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java new file mode 100644 index 0000000..3de142a --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_service.NestedStruct2InterfaceServiceAdapter; +import testbed2.testbed2jniservice.NestedStruct2InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NestedStruct2InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NestedStruct2InterfaceJniStarter"; + + + + public static INestedStruct2Interface start(Context context) { + stop(context); + androidService = new Intent(context, NestedStruct2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NestedStruct2InterfaceJniServiceFactory factory = NestedStruct2InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NestedStruct2InterfaceJniServiceFactory"); + return NestedStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java new file mode 100644 index 0000000..e45ac66 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java @@ -0,0 +1,185 @@ +package testbed2.testbed2jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_api.Enum3; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class NestedStruct3InterfaceJniService extends AbstractNestedStruct3Interface { + + + private final static String TAG = "NestedStruct3InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public NestedStruct3InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setProp1(NestedStruct1 prop1) + { + Log.i(TAG, "request setProp1 called, will call native "); + nativeSetProp1(prop1); + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, will call native "); + return nativeGetProp1(); + } + + + @Override + public void setProp2(NestedStruct2 prop2) + { + Log.i(TAG, "request setProp2 called, will call native "); + nativeSetProp2(prop2); + } + + @Override + public NestedStruct2 getProp2() + { + Log.i(TAG, "request getProp2 called, will call native "); + return nativeGetProp2(); + } + + + @Override + public void setProp3(NestedStruct3 prop3) + { + Log.i(TAG, "request setProp3 called, will call native "); + nativeSetProp3(prop3); + } + + @Override + public NestedStruct3 getProp3() + { + Log.i(TAG, "request getProp3 called, will call native "); + return nativeGetProp3(); + } + + + // methods + + @Override + public NestedStruct1 func1(NestedStruct1 param1) { + Log.w(TAG, "request method func1 called, will call native"); + return nativeFunc1(param1); + } + + @Override + public CompletableFuture func1Async(NestedStruct1 param1) { + return CompletableFuture.supplyAsync( + () -> {return func1(param1); }, + executor); + } + + @Override + public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { + Log.w(TAG, "request method func2 called, will call native"); + return nativeFunc2(param1, param2); + } + + @Override + public CompletableFuture func2Async(NestedStruct1 param1, NestedStruct2 param2) { + return CompletableFuture.supplyAsync( + () -> {return func2(param1, param2); }, + executor); + } + + @Override + public NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + Log.w(TAG, "request method func3 called, will call native"); + return nativeFunc3(param1, param2, param3); + } + + @Override + public CompletableFuture func3Async(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { + return CompletableFuture.supplyAsync( + () -> {return func3(param1, param2, param3); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetProp1(NestedStruct1 prop1); + private native NestedStruct1 nativeGetProp1(); + + private native void nativeSetProp2(NestedStruct2 prop2); + private native NestedStruct2 nativeGetProp2(); + + private native void nativeSetProp3(NestedStruct3 prop3); + private native NestedStruct3 nativeGetProp3(); + + // methods + private native NestedStruct1 nativeFunc1(NestedStruct1 param1); + private native NestedStruct1 nativeFunc2(NestedStruct1 param1, NestedStruct2 param2); + private native NestedStruct1 nativeFunc3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "onProp1Changed, will pass notification to all listeners"); + fireProp1Changed(newValue); + } + public void onProp2Changed(NestedStruct2 newValue) + { + Log.i(TAG, "onProp2Changed, will pass notification to all listeners"); + fireProp2Changed(newValue); + } + public void onProp3Changed(NestedStruct3 newValue) + { + Log.i(TAG, "onProp3Changed, will pass notification to all listeners"); + fireProp3Changed(newValue); + } + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "onSig1, will pass notification to all listeners"); + fireSig1(param1); + } + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(TAG, "onSig2, will pass notification to all listeners"); + fireSig2(param1, param2); + } + public void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) + { + Log.i(TAG, "onSig3, will pass notification to all listeners"); + fireSig3(param1, param2, param3); + } + +} diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java new file mode 100644 index 0000000..00df51c --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed2.testbed2jniservice; + +import testbed2.testbed2_android_service.INestedStruct3InterfaceServiceFactory; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_api.AbstractNestedStruct3Interface; +import testbed2.testbed2jniservice.NestedStruct3InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton NestedStruct3InterfaceJniServiceFactory thread for the system. This is a thread for + * NestedStruct3InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class NestedStruct3InterfaceJniServiceFactory extends HandlerThread implements INestedStruct3InterfaceServiceFactory +{ + private NestedStruct3InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static NestedStruct3InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: NestedStruct3InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractNestedStruct3Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new NestedStruct3InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new NestedStruct3InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final NestedStruct3InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private NestedStruct3InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static NestedStruct3InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + NestedStruct3InterfaceJniServiceFactory t = new NestedStruct3InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java new file mode 100644 index 0000000..046ef3b --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed2.testbed2jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_service.NestedStruct3InterfaceServiceAdapter; +import testbed2.testbed2jniservice.NestedStruct3InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class NestedStruct3InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "NestedStruct3InterfaceJniStarter"; + + + + public static INestedStruct3Interface start(Context context) { + stop(context); + androidService = new Intent(context, NestedStruct3InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + NestedStruct3InterfaceJniServiceFactory factory = NestedStruct3InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for NestedStruct3InterfaceJniServiceFactory"); + return NestedStruct3InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/build.gradle b/goldenmaster/testbed2/testbed2serviceexample/build.gradle new file mode 100644 index 0000000..d0466c2 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/build.gradle @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'testbed2.testbed2serviceexample' + compileSdk 35 + + defaultConfig { + applicationId "testbed2.testbed2serviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':testbed2_api') + implementation project(':testbed2_android_messenger') + implementation project(':testbed2_android_service') + implementation project(':testbed2_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/AndroidManifest.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..27dcfec --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java new file mode 100644 index 0000000..67a035f --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java @@ -0,0 +1,330 @@ +package testbed2.testbed2serviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import testbed2.testbed2_android_service.ManyParamInterfaceServiceAdapter; +import testbed2.testbed2_android_service.ManyParamInterfaceServiceFactory; +import testbed2.testbed2_android_service.ManyParamInterfaceServiceStarter; + +//import message type and parcelabe types +import testbed2.testbed2_api.Struct1; +import testbed2.testbed2_android_messenger.Struct1Parcelable; +import testbed2.testbed2_api.Struct2; +import testbed2.testbed2_android_messenger.Struct2Parcelable; +import testbed2.testbed2_api.Struct3; +import testbed2.testbed2_android_messenger.Struct3Parcelable; +import testbed2.testbed2_api.Struct4; +import testbed2.testbed2_android_messenger.Struct4Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +import testbed2.testbed2_api.NestedStruct3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; +import testbed2.testbed2_api.Enum1; +import testbed2.testbed2_android_messenger.Enum1Parcelable; +import testbed2.testbed2_api.Enum2; +import testbed2.testbed2_android_messenger.Enum2Parcelable; +import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.Enum3Parcelable; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import java.util.concurrent.CompletableFuture; + + + +public class Testbed2TestServiceApp extends Activity implements IManyParamInterfaceEventListener +{ + + private static final String TAG = "Testbed2TestServiceApp"; + static Intent stub_service = null; + + + private IManyParamInterface mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bProp1 = new Button(this); + bProp1.setText("Set prop1"); + bProp1.setBackgroundColor(Color.GREEN); + + bProp1.setOnClickListener(v -> { + int newProp1 = mBackend.getProp1(); + //TODO increment + Log.i(TAG, "SET prop1" + newProp1); + mBackend.setProp1(newProp1); + }); + propertyButtonsLine.addView(bProp1); + Button bProp2 = new Button(this); + bProp2.setText("Set prop2"); + bProp2.setBackgroundColor(Color.GREEN); + + bProp2.setOnClickListener(v -> { + int newProp2 = mBackend.getProp2(); + //TODO increment + Log.i(TAG, "SET prop2" + newProp2); + mBackend.setProp2(newProp2); + }); + propertyButtonsLine.addView(bProp2); + Button bProp3 = new Button(this); + bProp3.setText("Set prop3"); + bProp3.setBackgroundColor(Color.GREEN); + + bProp3.setOnClickListener(v -> { + int newProp3 = mBackend.getProp3(); + //TODO increment + Log.i(TAG, "SET prop3" + newProp3); + mBackend.setProp3(newProp3); + }); + propertyButtonsLine.addView(bProp3); + Button bProp4 = new Button(this); + bProp4.setText("Set prop4"); + bProp4.setBackgroundColor(Color.GREEN); + + bProp4.setOnClickListener(v -> { + int newProp4 = mBackend.getProp4(); + //TODO increment + Log.i(TAG, "SET prop4" + newProp4); + mBackend.setProp4(newProp4); + }); + propertyButtonsLine.addView(bProp4); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSig1 = new Button(this); + bSig1.setText("sig1"); + + bSig1.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig1 "); + int param1 = 1; + mBackend.fireSig1(param1); + }); + bSig1.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig1); + Button bSig2 = new Button(this); + bSig2.setText("sig2"); + + bSig2.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig2 "); + int param1 = 1; + int param2 = 1; + mBackend.fireSig2(param1, param2); + }); + bSig2.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig2); + Button bSig3 = new Button(this); + bSig3.setText("sig3"); + + bSig3.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig3 "); + int param1 = 1; + int param2 = 1; + int param3 = 1; + mBackend.fireSig3(param1, param2, param3); + }); + bSig3.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig3); + Button bSig4 = new Button(this); + bSig4.setText("sig4"); + + bSig4.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal sig4 "); + int param1 = 1; + int param2 = 1; + int param3 = 1; + int param4 = 1; + mBackend.fireSig4(param1, param2, param3, param4); + }); + bSig4.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bSig4); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, ManyParamInterfaceServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = ManyParamInterfaceServiceAdapter.setService(ManyParamInterfaceServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onProp1Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop1 " + newValue); + Log.w(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onProp2Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop2 " + newValue); + Log.w(TAG, "Property from service: prop2 " + newValue); + } + @Override + public void onProp3Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop3 " + newValue); + Log.w(TAG, "Property from service: prop3 " + newValue); + } + @Override + public void onProp4Changed(int newValue) + { + outputTextViewProp.setText("Property from service: prop4 " + newValue); + Log.w(TAG, "Property from service: prop4 " + newValue); + } + @Override + public void onSig1(int param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig2(int param1, int param2) + { + String text = "Signal sig2 "+ " " + param1+ " " + param2; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig3(int param1, int param2, int param3) + { + String text = "Signal sig3 "+ " " + param1+ " " + param2+ " " + param3; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void onSig4(int param1, int param2, int param3, int param4) + { + String text = "Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values-night/themes.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/colors.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/strings.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..a0c5cab --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Testbed2TestServiceApp + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/themes.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file From c886772472196dc7ea5e8bb677891023892284e9 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:23:10 +0200 Subject: [PATCH 33/45] feat: add github workflow --- .github/workflows/ci_run_tests.yml | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/ci_run_tests.yml diff --git a/.github/workflows/ci_run_tests.yml b/.github/workflows/ci_run_tests.yml new file mode 100644 index 0000000..69b9b57 --- /dev/null +++ b/.github/workflows/ci_run_tests.yml @@ -0,0 +1,77 @@ +# This workflow will checks that the goldenmaster passes all tests + +name: Test Goldenmaster + +on: + pull_request: + branches: [main] + +env: + GITHUB_AUTH_TOKEN: ${{ secrets.WOLFGANG_REPO_PACKAGE_READ }} + GOPRIVATE: "github.com/apigear-io/*" + GH_ACCESS_TOKEN: ${{ secrets.APIGEAR_REPOS }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + go_version: "1.21.x" + +jobs: + build: + runs-on: ubuntu-latest + env: + JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + # Set up JDK 17 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: temurin + + # set up go + - uses: actions/setup-go@v5 + with: + go-version: ${{env.go_version}} + + # Set up Android SDK (needed for AGP and compileSdk) + - name: Set up Android SDK + uses: android-actions/setup-android@v2 + with: + api-level: 34 + build-tools: 34.0.0 + ndk: 25.2.9519653 + components: platform-tools,cmdline-tools + + # Set up Gradle + - name: Set up Gradle + uses: gradle/gradle-build-action@v3 + with: + gradle-version: '8.10' # Quotes required to prevent YAML converting to number + use-wrapper: false + # remove auto generated gradle files before the gradle version is set + - name: Clean any generated wrappers + run: | + find goldenmaster -type d -name wrapper -exec rm -rf {} + + echo "Removed any gradle/wrapper folders in submodules" + + # Build all modules + - name: Build all modules + working-directory: goldenmaster + run: gradle runAll --stacktrace + + # Run all unit tests (JVM tests for Android modules) + - name: Run all tests + working-directory: goldenmaster + run: gradle runJavaUnitTests --stacktrace + + # Upload JUnit / Robolectric test reports as artifacts + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: 'goldenmaster/**/build/test-results/test' From f0030408e27b9ff897af7057698cbd0d19b02383 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 26 Sep 2025 13:22:01 +0200 Subject: [PATCH 34/45] fix(jni): fix unbind, to work even if called multiple times --- .../tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java | 5 ++++- goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java | 5 ++++- .../tbSame1jniclient/SameEnum1InterfaceJniClient.java | 5 ++++- .../tbSame1jniclient/SameEnum2InterfaceJniClient.java | 5 ++++- .../tbSame1jniclient/SameStruct1InterfaceJniClient.java | 5 ++++- .../tbSame1jniclient/SameStruct2InterfaceJniClient.java | 5 ++++- .../tbSame2jniclient/SameEnum1InterfaceJniClient.java | 5 ++++- .../tbSame2jniclient/SameEnum2InterfaceJniClient.java | 5 ++++- .../tbSame2jniclient/SameStruct1InterfaceJniClient.java | 5 ++++- .../tbSame2jniclient/SameStruct2InterfaceJniClient.java | 5 ++++- .../tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java | 5 ++++- .../tbSimplejniclient/NoOperationsInterfaceJniClient.java | 5 ++++- .../tbSimplejniclient/NoPropertiesInterfaceJniClient.java | 5 ++++- .../tbSimplejniclient/NoSignalsInterfaceJniClient.java | 5 ++++- .../tbSimplejniclient/SimpleArrayInterfaceJniClient.java | 5 ++++- .../tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java | 5 ++++- .../tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java | 5 ++++- .../testbed1jniclient/StructArrayInterfaceJniClient.java | 5 ++++- .../testbed1/testbed1jniclient/StructInterfaceJniClient.java | 5 ++++- .../testbed2jniclient/ManyParamInterfaceJniClient.java | 5 ++++- .../testbed2jniclient/NestedStruct1InterfaceJniClient.java | 5 ++++- .../testbed2jniclient/NestedStruct2InterfaceJniClient.java | 5 ++++- .../testbed2jniclient/NestedStruct3InterfaceJniClient.java | 5 ++++- templates/jnibridge/client/jnibridgeclient.java.tpl | 5 ++++- 24 files changed, 96 insertions(+), 24 deletions(-) diff --git a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java index 114d67b..44a8f15 100644 --- a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java +++ b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java @@ -165,7 +165,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java index 6e3493f..93a8e27 100644 --- a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java +++ b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java @@ -112,7 +112,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java index 79395db..e2df979 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java @@ -72,7 +72,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java index 1542776..34999b4 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java @@ -103,7 +103,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java index 51d1c18..3e7bc7b 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java @@ -72,7 +72,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java index 619fb09..b060b41 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java @@ -103,7 +103,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java index bf75c2b..5c92921 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java @@ -72,7 +72,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java index ddf8c02..ad0823a 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java @@ -103,7 +103,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java index 3281be5..ccc6e80 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java @@ -72,7 +72,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java index 1514f16..b324957 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java @@ -103,7 +103,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java index fcc3fec..ebbb128 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java @@ -37,7 +37,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java index 40e43fc..9acb14e 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java @@ -63,7 +63,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java index 9197d3d..214a0c4 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java @@ -73,7 +73,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java index 9c8de6a..6cdc249 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java @@ -99,7 +99,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java index d6018b8..4299805 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java @@ -298,7 +298,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java index 0dee43b..cdab0e0 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java @@ -303,7 +303,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java index 5d842af..94df48a 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java @@ -55,7 +55,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java index 66a12a6..887eb91 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java @@ -165,7 +165,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java index 16e63b6..5abf194 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java @@ -165,7 +165,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java index 0b78410..b836a46 100644 --- a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java @@ -171,7 +171,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java index 7291643..06934cc 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -78,7 +78,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java index 5147d73..bc42e80 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java @@ -109,7 +109,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java index 0de91cc..6118486 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java @@ -140,7 +140,10 @@ public boolean bind(Context ctx, String packageName, String connectionID){ public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) diff --git a/templates/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl index 9a004ef..df11153 100644 --- a/templates/jnibridge/client/jnibridgeclient.java.tpl +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -80,7 +80,10 @@ public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interfa public void unbind(){ Log.v(TAG, "native client: unbind " + lastServicePackage); - mMessengerClient.unbindFromService(); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } } private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) From 93a34a2f06547302c3591858df80eabcfe6ab7fd Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:48:05 +0200 Subject: [PATCH 35/45] fix(jni): clean up the static members for service adapter --- .../android/service/serviceadapter.java.tpl | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index 4a79d57..36d9f26 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -38,10 +38,11 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static I{{Camel .Interface.Name}} mBackendService; private static I{{Camel .Interface.Name}}ServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -56,10 +57,19 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService({{Camel .Interface.Name}}) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -70,13 +80,25 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate({{Camel .Interface.Name }}Service) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -93,18 +115,21 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy({{Camel .Interface.Name }}Service) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy({{Camel .Interface.Name }}Service) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy({{Camel .Interface.Name }}Service) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override From 5be1cb81fae043213983a4f674188910fff18aae Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:53:46 +0200 Subject: [PATCH 36/45] feat: support imports and externs --- apigear/goldenmaster.solution.yaml | 7 +- rules.yaml | 7 + templates/android/client/additions.gradle.tpl | 4 + templates/android/client/build.gradle.tpl | 3 + templates/android/client/client.java.tpl | 131 ++++++++++---- templates/android/client/clienttest.java.tpl | 61 ++++--- .../android/messenger/additions.gradle.tpl | 3 + templates/android/messenger/build.gradle.tpl | 3 + .../messenger/externparcelable.java.tpl | 68 +++++++ .../messenger/interfaceparcelable.java.tpl | 170 ++++++++++++++++++ .../messenger/structparcelable.java.tpl | 13 +- .../android/service/additions.gradle.tpl | 13 +- templates/android/service/build.gradle.tpl | 10 +- .../android/service/serviceadapter.java.tpl | 67 +++++-- .../service/serviceadaptertest.java.tpl | 37 ++-- templates/api/abstract.java.tpl | 2 +- templates/api/additions.gradle.tpl | 9 + templates/api/build.gradle.tpl | 9 + templates/api/testhelper.java.tpl | 40 ++++- .../jnibridge/client/jnibridgeclient.java.tpl | 57 +++++- .../service/jnibridgeservice.java.tpl | 58 +++++- templates/settings.gradle.tpl | 2 + templates/stub/additions.gradle.tpl | 1 + templates/stub/implservice.java.tpl | 56 +++++- templates/testclientapp/application.java.tpl | 63 ++++++- templates/testclientapp/build.gradle.tpl | 3 + templates/testserviceapp/application.java.tpl | 62 ++++++- templates/testserviceapp/build.gradle.tpl | 4 + test-apis | 2 +- 29 files changed, 845 insertions(+), 120 deletions(-) create mode 100644 templates/android/messenger/externparcelable.java.tpl create mode 100644 templates/android/messenger/interfaceparcelable.java.tpl diff --git a/apigear/goldenmaster.solution.yaml b/apigear/goldenmaster.solution.yaml index bbdafe8..c1292ba 100644 --- a/apigear/goldenmaster.solution.yaml +++ b/apigear/goldenmaster.solution.yaml @@ -14,7 +14,12 @@ targets: - ../test-apis/testbed.same2.module.yaml - ../test-apis/testbed.simple.module.yaml - ../test-apis/testbed.struct.module.yaml - output: ../test2 + - ../test-apis/custom_types.module.yaml + - ../test-apis/extern_types.module.yaml + - ../test-apis/counter.module.yaml + - ../test-apis/testbed.ifaceimport.module.yaml + - ../test-apis/testbed.refifaces.module.yaml + output: ../test template: .. force: true features: ["all"] diff --git a/rules.yaml b/rules.yaml index 9e3bbf3..8877125 100644 --- a/rules.yaml +++ b/rules.yaml @@ -96,6 +96,8 @@ features: documents: - source: "android/messenger/messages.java.tpl" target: "{{Camel .Interface.Name}}MessageType.java" + - source: "android/messenger/interfaceparcelable.java.tpl" + target: "{{Camel .Interface.Name }}Parcelable.java" - match: struct prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/" documents: @@ -106,6 +108,11 @@ features: documents: - source: "android/messenger/enumparcelable.java.tpl" target: "{{Camel .Enum.Name }}Parcelable.java" + - match: extern + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/src/main/java/{{camel .Module.Name}}/{{camel .Module.Name}}_android_messenger/" + documents: + - source: "android/messenger/externparcelable.java.tpl" + target: "{{Camel .Extern.Name }}Parcelable.java" - match: module prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_android_client/" documents: diff --git a/templates/android/client/additions.gradle.tpl b/templates/android/client/additions.gradle.tpl index a8a8c36..386dd13 100644 --- a/templates/android/client/additions.gradle.tpl +++ b/templates/android/client/additions.gradle.tpl @@ -8,10 +8,14 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') implementation project(':{{camel .Module.Name}}_android_messenger') + {{- range .Module.Imports}} + api project(':{{camel .Name}}_android_messenger') + {{- end }} testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/client/build.gradle.tpl b/templates/android/client/build.gradle.tpl index 800115c..6f6d0ee 100644 --- a/templates/android/client/build.gradle.tpl +++ b/templates/android/client/build.gradle.tpl @@ -28,6 +28,9 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') implementation project(':{{camel .Module.Name}}_android_messenger') + {{- range .Module.Imports}} + api '{{camel .Name}}:{{camel .Name}}_android_messenger:{{ ($.System.LookupModule .Name).Version }}' + {{- end }} testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 86bb956..db09ff9 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -18,14 +18,61 @@ import android.util.Log; //import message type and parcelabe types -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- range .Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range .Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} {{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; @@ -46,9 +93,9 @@ import java.util.Arrays; {{- if .IsPrimitive }} {{javaReturn "" .}} {{javaVar .}} = data.get{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}"{{if not .IsArray}}, {{javaDefault "" .}}{{end}}); {{- else if .IsArray }} - {{javaReturn "" .}} {{javaVar .}} = {{Camel .Type}}Parcelable.unwrapArray(({{Camel .Type}}Parcelable[])data.getParcelableArray("{{.Name}}", {{Camel .Type}}Parcelable.class)); + {{javaReturn "" .}} {{javaVar .}} = {{template "getParcelable" . }}.unwrapArray(({{template "getParcelable" . }}[])data.getParcelableArray("{{.Name}}", {{template "getParcelable" . }}.class)); {{- else }} - {{javaReturn "" .}} {{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{javaReturn "" .}} {{javaVar .}} = data.getParcelable("{{.Name}}", {{template "getParcelable" . }}.class).get{{Camel .Type}}(); {{- end }} {{- end }} @@ -56,9 +103,9 @@ import java.util.Arrays; {{- if and .IsPrimitive }} data.put{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}", {{ javaVar .}}); {{- else if .IsArray }} - data.putParcelableArray("{{.Name}}", {{Camel (javaElementType "" .) }}Parcelable.wrapArray({{javaVar .}})); + data.putParcelableArray("{{.Name}}", {{template "getParcelable" . }}.wrapArray({{javaVar .}})); {{- else }} - data.putParcelable("{{.Name}}", new {{Camel (javaElementType "" .) }}Parcelable({{javaVar .}})); + data.putParcelable("{{.Name}}", new {{template "getParcelable" . }}({{javaVar .}})); {{- end }} {{- end }} @@ -66,9 +113,9 @@ import java.util.Arrays; {{- if and .Return.IsPrimitive }} {{javaReturn "" .Return }} result = bundle.get{{ ( Camel (javaElementType "" .Return ) ) }}{{if .Return.IsArray}}Array{{end}}("result"{{if not .Return.IsArray}}, {{javaDefault "" .Return}}{{end}}); {{- else if .Return.IsArray }} - {{javaReturn "" .Return}} result = {{Camel (javaElementType "" .Return) }}Parcelable.unwrapArray(({{Camel .Return.Type}}Parcelable[])bundle.getParcelableArray("result", {{Camel .Return.Type}}Parcelable.class)); + {{javaReturn "" .Return}} result = {{template "getParcelable" .Return }}.unwrapArray(({{template "getParcelable" .Return }}[])bundle.getParcelableArray("result", {{template "getParcelable" .Return }}.class)); {{- else }} - {{javaReturn "" .Return }} result = bundle.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); + {{javaReturn "" .Return }} result = bundle.getParcelable("result", {{template "getParcelable" .Return }}.class).get{{Camel .Return.Type}}(); {{- end }} {{- end }} @@ -76,13 +123,49 @@ import java.util.Arrays; {{- if and .Return.IsPrimitive }} resp_data.put{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result", result); {{- else if .Return.IsArray }} - resp_data.putParcelableArray("result",{{Camel (javaElementType "" .Return) }}Parcelable.wrapArray(result)); + resp_data.putParcelableArray("result",{{template "getParcelable" .Return }}.wrapArray(result)); {{- else }} - resp_data.putParcelable("result", new {{Camel (javaElementType "" .Return) }}Parcelable(result)); + resp_data.putParcelable("result", new {{template "getParcelable" .Return }}(result)); {{- end }} {{- end }} +{{- define "setClassLoaderIfNeeded" }} + {{- $numOfStructsSameModule := 0 }} + {{- $numOfStructsOtherModule := 0 }} + {{- $parcelableFromSameModule := "" }} + {{- $parcelableFromOtherModule := "" }} + {{- range .}} + {{- if not .IsPrimitive }} + {{- if (eq (.Schema.Import ) "" ) -}} + {{- if eq $numOfStructsSameModule 0 }} + {{- $parcelableFromSameModule = . }} + {{- end }} + {{- $numOfStructsSameModule = len (printf "%*s " $numOfStructsSameModule "") }} + {{- else }} + {{- if eq $numOfStructsOtherModule 0 }} + {{- $parcelableFromOtherModule = . }} + {{- end }} + {{- $numOfStructsOtherModule = len (printf "%*s " $numOfStructsOtherModule "") }} + {{- end }} + {{- end }} + {{- end }} + + {{- if $numOfStructsSameModule }} + {{- if ge $numOfStructsOtherModule 1 }} + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + {{- end }} + data.setClassLoader({{template "getParcelable" $parcelableFromSameModule }}.class.getClassLoader()); + + {{- else if $numOfStructsOtherModule}} + {{- if ge $numOfStructsOtherModule 1 }} + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + {{- end }} + data.setClassLoader({{template "getParcelable" $parcelableFromOtherModule }}.class.getClassLoader()); + {{- end }} +{{- end }} + + public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface.Name}} implements ServiceConnection { private static final String TAG = "{{Camel .Interface.Name }}Client"; @@ -234,11 +317,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface case INIT: { Bundle data = msg.getData(); - {{range .Interface.Properties}} - {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Interface.Properties}} {{range .Interface.Properties}} {{template "getDataFromBundle" . }} on{{Camel .Name}}({{javaVar .}}); @@ -252,7 +331,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface Bundle data = msg.getData(); {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + data.setClassLoader({{template "getParcelable" . }}.class.getClassLoader()); {{- end }} {{template "getDataFromBundle" . }} @@ -268,11 +347,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface case SIG_{{Camel .Name}}: { Bundle data = msg.getData(); - {{- range .Params }} - {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Params}} {{- range .Params }} {{template "getDataFromBundle" . }} {{- end }} @@ -284,11 +359,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface case RPC_{{Camel .Name}}Resp: { Bundle data = msg.getData(); - {{- range .Params }} - {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Params}} int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index ed2de85..0a650cb 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -63,9 +63,9 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter {{- if .IsPrimitive }} {{javaReturn "" .}} received{{javaVar .}} = data.get{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}"{{if not .IsArray}}, {{javaDefault "" .}}{{end}}); {{- else if .IsArray }} - {{javaReturn "" .}} received{{javaVar .}} = {{Camel .Type}}Parcelable.unwrapArray(({{Camel .Type}}Parcelable[])data.getParcelableArray("{{.Name}}", {{Camel .Type}}Parcelable.class)); + {{javaReturn "" .}} received{{javaVar .}} = {{template "getParcelable" . }}.unwrapArray(({{template "getParcelable" . }}[])data.getParcelableArray("{{.Name}}", {{template "getParcelable" . }}.class)); {{- else }} - {{javaReturn "" .}} received{{javaVar .}} = data.getParcelable("{{.Name}}", {{Camel .Type}}Parcelable.class).get{{Camel (javaReturn "" .)}}(); + {{javaReturn "" .}} received{{javaVar .}} = data.getParcelable("{{.Name}}", {{template "getParcelable" . }}.class).get{{Camel (.Type)}}(); {{- end }} {{- end }} @@ -73,9 +73,9 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter {{- if .IsPrimitive }} data.put{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}", test{{ javaVar .}}); {{- else if .IsArray }} - data.putParcelableArray("{{.Name}}", {{Camel (javaElementType "" .) }}Parcelable.wrapArray(test{{javaVar .}})); + data.putParcelableArray("{{.Name}}", {{template "getParcelable" . }}.wrapArray(test{{javaVar .}})); {{- else }} - data.putParcelable("{{.Name}}", new {{Camel (javaElementType "" .) }}Parcelable(test{{javaVar .}})); + data.putParcelable("{{.Name}}", new {{template "getParcelable" . }}(test{{javaVar .}})); {{- end }} {{- end }} @@ -86,12 +86,18 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter test{{ javaVar .}}[0] = {{javaTestValue "" . }}; {{- else }} {{javaElementType "" .}}[] test{{ javaVar .}} = new {{javaElementType "" .}}[1]; - test{{ javaVar .}}[0] = {{Camel .Schema.Module.Name}}TestHelper.makeTest{{Camel (javaElementType "" . )}}(); + {{- if (eq .KindType "extern") }} + test{{ javaVar .}}[0] = {{javaTestValue "" .}}; + {{- else }} + test{{ javaVar .}}[0] = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaDefault "" .}}{{end}}); + {{- end }} {{- end}} {{- else if or (.IsPrimitive) (eq .KindType "enum") }} {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; - {{- else }} - {{javaReturn "" . }} test{{ javaVar .}} = {{Camel .Schema.Module.Name}}TestHelper.makeTest{{Camel (javaType "" . )}}(); + {{- else if (eq .KindType "extern") }} + {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" .}}; + {{- else }} + {{javaReturn "" . }} test{{ javaVar .}} = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaDefault "" .}}{{end}}); {{- end }} {{- end }} @@ -195,13 +201,15 @@ public class {{Camel .Interface.Name }}ClientTest {{- if or (.IsPrimitive) (eq .KindType "enum") }} inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(test{{javaVar .}}); {{- else }} + {{ if or (eq .KindType "extern") (eq .KindType "interface")}} + // Make sure test data is properly filled and in case of extern serialization is in place. + //{{end -}} inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(any({{javaReturn "" . }}.class)); {{- end }} {{- end }} } {{- range .Interface.Properties }} -//TODO do not add when a property is readonly @Test public void onReceive{{.Name}}PropertyChangeTest() throws RemoteException { // Create and send message @@ -216,10 +224,16 @@ public class {{Camel .Interface.Name }}ClientTest {{- if or (.IsPrimitive) (eq .KindType "enum") }} inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(test{{javaVar .}}); {{- else }} + {{ if or (eq .KindType "extern") (eq .KindType "interface")}} + // Make sure test data is properly filled and in case of extern serialization is in place. + //{{end -}} inOrderEventListener.verify(listenerMock,times(1)).on{{Camel .Name}}Changed(any({{javaReturn "" . }}.class)); {{- end }} } - + {{- if not .IsReadOnly }} + {{ if or (eq .KindType "extern") (eq .KindType "interface")}} + /* + {{- end }} @Test public void setPropertyRequest{{.Name}}() { @@ -227,14 +241,13 @@ public class {{Camel .Interface.Name }}ClientTest testedClient.set{{Camel .Name}}(test{{ javaVar .}}); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); assertEquals({{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(), response.what); Bundle data = response.getData(); {{- if not (.IsPrimitive) }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + data.setClassLoader({{template "getParcelable" . }}.class.getClassLoader()); {{- end }} {{template "getReceivedFromBundle" . }} assertEquals(received{{javaVar .}}, test{{ javaVar .}} @@ -242,6 +255,10 @@ public class {{Camel .Interface.Name }}ClientTest , 1e-6f{{end -}} ); } + {{ if or (eq .KindType "extern") (eq .KindType "interface")}} + */ + {{- end }} + {{- end }} {{- end}} {{- range .Interface.Signals }} @@ -286,12 +303,18 @@ public class {{Camel .Interface.Name }}ClientTest expectedResult[0] = {{javaTestValue "" .Return }}; {{- else }} {{javaElementType "" .Return }}[] expectedResult = new {{javaElementType "" .Return }}[1]; - expectedResult[0] = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaElementType "" .Return )}}(); + {{- if (eq .Return.KindType "extern") }} + expectedResult[0] = {{javaTestValue "" .Return}}; + {{- else }} + expectedResult[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaDefault "" .Return}}{{end}}); + {{- end }} {{- end}} {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return }}; - {{- else }} - {{javaReturn "" .Return }} expectedResult = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaType "" .Return )}}(); + {{- else if (eq .Return.KindType "extern") }} + {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .Return}}; + {{- else }} + {{javaReturn "" .Return }} expectedResult = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaDefault "" .Return}}{{end}}); {{- end }} {{- end }} @@ -322,11 +345,7 @@ public class {{Camel .Interface.Name }}ClientTest Message method_request = messageCaptor.getValue(); assertEquals({{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Req.getValue(), method_request.what); Bundle data = method_request.getData(); - {{- range .Params }} - {{- if not (.IsPrimitive) }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Params}} {{- range .Params }} {{template "getReceivedFromBundle" . }} assertEquals(received{{javaVar .}}, test{{javaVar .}} @@ -347,9 +366,9 @@ public class {{Camel .Interface.Name }}ClientTest {{- if .Return.IsPrimitive }} result_data.put{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result", expectedResult); {{- else if .Return.IsArray }} - result_data.putParcelableArray("result", {{Camel (javaElementType "" .Return) }}Parcelable.wrapArray(expectedResult)); + result_data.putParcelableArray("result", {{template "getParcelable" .Return }}.wrapArray(expectedResult)); {{- else }} - result_data.putParcelable("result", new {{Camel (javaElementType "" .Return) }}Parcelable(expectedResult)); + result_data.putParcelable("result", new {{template "getParcelable" .Return }}(expectedResult)); {{- end }} {{- end }} diff --git a/templates/android/messenger/additions.gradle.tpl b/templates/android/messenger/additions.gradle.tpl index 252c9a8..b186f58 100644 --- a/templates/android/messenger/additions.gradle.tpl +++ b/templates/android/messenger/additions.gradle.tpl @@ -11,6 +11,9 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') + {{- range .Module.Imports}} + api project(':{{camel .Name}}_android_messenger') + {{- end }} testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/messenger/build.gradle.tpl b/templates/android/messenger/build.gradle.tpl index b1c8be4..4dc5073 100644 --- a/templates/android/messenger/build.gradle.tpl +++ b/templates/android/messenger/build.gradle.tpl @@ -27,4 +27,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + {{- range .Module.Imports}} + api '{{camel .Name}}:{{camel .Name}}_android_messenger:{{ ($.System.LookupModule .Name).Version }}' + {{- end }} } diff --git a/templates/android/messenger/externparcelable.java.tpl b/templates/android/messenger/externparcelable.java.tpl new file mode 100644 index 0000000..1d1de92 --- /dev/null +++ b/templates/android/messenger/externparcelable.java.tpl @@ -0,0 +1,68 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; + +{{- $externInfo := javaExtern .Extern }} +import {{$externInfo.Package}}.{{$externInfo.Name}}; +import android.os.Parcel; +import android.os.Parcelable; + + + public class {{Camel .Extern.Name}}Parcelable implements Parcelable { + public {{$externInfo.Name}} data; + + public {{Camel .Extern.Name}}Parcelable({{$externInfo.Name}} data) { + // WARNING Copy if not simple type + this.data = data; + } + + public {{$externInfo.Name}} get{{Camel .Extern.Name}}() + { + // WARNING Copy if not simple type. + return data; + } + + protected {{Camel .Extern.Name}}Parcelable(Parcel in) { + //WARNING Fill the data field by field with in.createTypedArray, in. read[dataType] or in.readParcelable, depending on type. + } + + public static final Creator<{{Camel .Extern.Name}}Parcelable> CREATOR = new Creator<{{Camel .Extern.Name}}Parcelable>() { + @Override + public {{Camel .Extern.Name}}Parcelable createFromParcel(Parcel in) { + return new {{Camel .Extern.Name}}Parcelable(in); + } + + @Override + public {{Camel .Extern.Name}}Parcelable[] newArray(int size) { + return new {{Camel .Extern.Name}}Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + // WARNING Fill dest field by field with dest.write[TypedArray/Type/Parcelabe](data.field, flags); + } + + // Helpers for arrays of this type + public static {{Camel .Extern.Name}}Parcelable[] wrapArray({{$externInfo.Name}}[] elements) + { + if (elements == null) return null; + {{Camel .Extern.Name}}Parcelable[] out = new {{Camel .Extern.Name}}Parcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new {{Camel .Extern.Name}}Parcelable(elements[i]); + } + return out; + } + + public static {{$externInfo.Name}}[] unwrapArray({{Camel .Extern.Name}}Parcelable[] parcelables) { + if (parcelables == null) return null; + {{$externInfo.Name}}[] out = new {{$externInfo.Name}}[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].get{{Camel .Extern.Name}}(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/templates/android/messenger/interfaceparcelable.java.tpl b/templates/android/messenger/interfaceparcelable.java.tpl new file mode 100644 index 0000000..34a4fa3 --- /dev/null +++ b/templates/android/messenger/interfaceparcelable.java.tpl @@ -0,0 +1,170 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; + +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name}}; +import android.os.Parcel; +import android.os.Parcelable; + + +{{- define "getParcelable"}} + {{- $ImportSchema:= printf "%s" ( camel .Schema.Import ) }} + {{- $parcelableTypeName := Camel .Type -}} + {{- if not (eq $ImportSchema "" ) -}} + {{$ImportSchema}}.{{$ImportSchema}}_android_messenger.{{$parcelableTypeName}}Parcelable + {{- else -}} + {{$parcelableTypeName}}Parcelable + {{- end -}} +{{- end }} +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range .Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range .Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +{{- end}} + + public class {{Camel .Interface.Name}}Parcelable implements Parcelable { + + public I{{Camel .Interface.Name}} data; + + public {{Camel .Interface.Name}}Parcelable(I{{Camel .Interface.Name}} data) { + this.data = data; + } + + public I{{Camel .Interface.Name}} get{{Camel .Interface.Name}}() + { + return data; + } + + protected {{Camel .Interface.Name}}Parcelable(Parcel in) { +{{- range .Interface.Properties }} +{{- if .IsArray}} +{{- if (eq .KindType "enum") }} + {{template "getParcelable" .}}[] l_parcelable{{camel .Name}} = in.createTypedArray({{template "getParcelable" .}}.CREATOR); + data.set{{Camel .Name}}({{template "getParcelable" .}}.unwrapArray(l_parcelable{{camel .Name}})); +{{- else if .IsPrimitive }} + data.set{{Camel .Name}}(in.create{{ ( Camel (javaElementType "" .) ) }}Array()); +{{- else }} + {{template "getParcelable" .}}[] l_parcelable{{camel .Name}} = in.createTypedArray({{template "getParcelable" .}}.CREATOR); + data.set{{Camel .Name}}({{template "getParcelable" .}}.unwrapArray(l_parcelable{{camel .Name}})); +{{- end }} +{{- else }} +{{- if (eq .KindType "enum") }} + {{template "getParcelable" .}} l_parcelable{{camel .Name}} = in.readParcelable({{template "getParcelable" .}}.class.getClassLoader(), {{template "getParcelable" .}}.class); + data.set{{Camel .Name}}(l_parcelable{{camel .Name}} != null ? l_parcelable{{camel .Name}}.data : null); +{{- else if .IsPrimitive }} + data.set{{Camel .Name}}(in.read{{ ( Camel (javaType "" .) ) }}()); +{{- else }} + {{template "getParcelable" .}} l_parcelable{{camel .Name}} = in.readParcelable({{template "getParcelable" .}}.class.getClassLoader(), {{template "getParcelable" .}}.class); + data.set{{Camel .Name}}(l_parcelable{{camel .Name}} != null ? l_parcelable{{camel .Name}}.data : null); +{{- end }} +{{- end }} + +{{- end }} + } + + public static final Creator<{{Camel .Interface.Name}}Parcelable> CREATOR = new Creator<{{Camel .Interface.Name}}Parcelable>() { + @Override + public {{Camel .Interface.Name}}Parcelable createFromParcel(Parcel in) { + return new {{Camel .Interface.Name}}Parcelable(in); + } + + @Override + public {{Camel .Interface.Name}}Parcelable[] newArray(int size) { + return new {{Camel .Interface.Name}}Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + {{- range .Interface.Properties }} +{{- if .IsArray}} +{{- if (eq .KindType "enum") }} + dest.writeTypedArray({{template "getParcelable" .}}.wrapArray(data.get{{Camel .Name}}()), flags); +{{- else if .IsPrimitive }} + dest.write{{ ( Camel (javaElementType "" .) ) }}Array(data.get{{Camel .Name}}()); +{{- else }} + dest.writeTypedArray({{template "getParcelable" .}}.wrapArray(data.get{{Camel .Name}}()), flags); +{{- end }} +{{- else }} +{{- if (eq .KindType "enum") }} + dest.writeParcelable(new {{template "getParcelable" .}}(data.get{{Camel .Name}}()), flags); +{{- else if .IsPrimitive }} + dest.write{{ ( Camel (javaType "" .) ) }}(data.get{{Camel .Name}}()); +{{- else }} + dest.writeParcelable(new {{template "getParcelable" .}}(data.get{{Camel .Name}}()), flags); +{{- end }} +{{- end }} + +{{- end}} + + + } + public static {{Camel .Interface.Name}}Parcelable[] wrapArray(I{{Camel .Interface.Name}}[] elements) { + if (elements == null) return null; + {{Camel .Interface.Name}}Parcelable[] out = new {{Camel .Interface.Name}}Parcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new {{Camel .Interface.Name}}Parcelable(elements[i]); + } + return out; + } + + public static I{{Camel .Interface.Name}}[] unwrapArray({{Camel .Interface.Name}}Parcelable[] parcelables) { + if (parcelables == null) return null; + I{{Camel .Interface.Name}}[] out = new I{{Camel .Interface.Name}}[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].get{{Camel .Interface.Name}}(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/templates/android/messenger/structparcelable.java.tpl b/templates/android/messenger/structparcelable.java.tpl index 7a868dc..67f672f 100644 --- a/templates/android/messenger/structparcelable.java.tpl +++ b/templates/android/messenger/structparcelable.java.tpl @@ -5,16 +5,25 @@ import android.os.Parcel; import android.os.Parcelable; {{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} {{- $module := camel .Module.Name}} {{- range .Struct.Fields }} -{{- if and (and (not .IsArray) (not .Schema.Import)) (not (or (.IsPrimitive) (eq .KindType "enum")) ) }} -{{- $type := Camel (javaType "" .) }} +{{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} +{{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} +{{- else }} {{- $typesToImport = (appendList $typesToImport $type) }} {{- end }} {{- end }} +{{- end }} {{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} {{- range $typesToImport}} import {{$module}}.{{$module}}_api.{{.}}; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; {{- end}} public class {{Camel .Struct.Name}}Parcelable implements Parcelable { diff --git a/templates/android/service/additions.gradle.tpl b/templates/android/service/additions.gradle.tpl index d5aae2f..5480129 100644 --- a/templates/android/service/additions.gradle.tpl +++ b/templates/android/service/additions.gradle.tpl @@ -11,8 +11,17 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') - implementation project(':{{camel .Module.Name}}_impl') - implementation project(':{{camel .Module.Name}}_android_messenger') + {{- if len (.Module.Interfaces)}} + api project(':{{camel .Module.Name}}_impl') + {{- end }} + api project(':{{camel .Module.Name}}_android_messenger') + {{- range .Module.Imports}} + api project(':{{camel .Name}}_android_messenger') + {{- $importModule := ($.System.LookupModule .Name)}} + {{- if len $importModule.Interfaces}} + api project(':{{camel .Name}}_impl') + {{- end }} + {{- end }} testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/service/build.gradle.tpl b/templates/android/service/build.gradle.tpl index 2e27bec..03c34eb 100644 --- a/templates/android/service/build.gradle.tpl +++ b/templates/android/service/build.gradle.tpl @@ -5,7 +5,6 @@ plugins { group = "{{camel .Module.Name}}" version = "{{.Module.Version}}" - android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_android_service' compileSdk 35 @@ -28,8 +27,17 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':{{camel .Module.Name}}_api') + {{- if len (.Module.Interfaces)}} implementation project(':{{camel .Module.Name}}_impl') + {{- end }} implementation project(':{{camel .Module.Name}}_android_messenger') + {{- range .Module.Imports}} + api '{{camel .Name}}:{{camel .Name}}_android_messenger:{{ ($.System.LookupModule .Name).Version }}' + {{- $importModule := ($.System.LookupModule .Name) }} + {{- if len $importModule.Interfaces}} + implementation '{{camel .Name}}:{{camel .Name}}_impl:{{ $importModule.Version }}' + {{- end }} + {{- end }} testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index 36d9f26..aa52c37 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -15,14 +15,61 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- range .Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} +{{- range .Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; @@ -217,7 +264,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service { Bundle data = msg.getData(); {{- if not (.IsPrimitive)}} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); + data.setClassLoader({{template "getParcelable" . }}.class.getClassLoader()); {{- end}} {{template "getDataFromBundle" . }} mBackendService.set{{Camel .Name}}({{javaVar .}}); @@ -231,11 +278,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service case RPC_{{Camel .Name}}Req: { Bundle data = msg.getData(); - {{- range .Params }} - {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Params}} int callId = data.getInt("callId"); {{- range .Params }} diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 7abfa6d..69bb8ae 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -188,11 +188,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- range .Interface.Properties}} {{ template "getReceivedFromBundle" .}} {{- end }} - {{- range .Interface.Properties}} - {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Interface.Properties}} {{- range .Interface.Properties}} {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} assertEquals(received{{javaVar .}}, init{{ javaVar .}} @@ -248,7 +244,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- end }} {{- range .Interface.Properties }} -//TODO do not add when a property is readonly + {{- if not .IsReadOnly }} @Test public void onReceive{{.Name}}PropertyChangeTest() throws RemoteException { // Create and send message @@ -264,6 +260,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest inOrderBackendService.verify(backendServiceMock,times(1)).set{{Camel .Name}}({{- if or (.IsPrimitive) (eq .KindType "enum") }}test{{javaVar .}}{{- else }} any({{javaReturn "" . }}.class) {{- end -}}); } + {{- end }} @Test public void whenNotified{{.Name}}() @@ -304,11 +301,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest assertEquals({{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue(), response.what); Bundle data = response.getData(); - {{- range .Params}} - {{- if not .IsPrimitive }} - data.setClassLoader({{Camel .Type}}Parcelable.class.getClassLoader()); - {{- end }} - {{- end }} + {{template "setClassLoaderIfNeeded" .Params}} {{- range .Params }} {{template "getReceivedFromBundle" . }} assertEquals(received{{javaVar .}}, test{{ javaVar .}} @@ -342,12 +335,18 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest returnedValue[0] = {{javaTestValue "" .Return }}; {{- else }} {{javaElementType "" .Return }}[] returnedValue = new {{javaElementType "" .Return }}[1]; - returnedValue[0] = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaElementType "" .Return )}}(); + {{- if (eq .Return.KindType "extern") }} + returnedValue[0] = {{javaTestValue "" .Return}}; + {{- else }} + returnedValue[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaDefault "" .Return}}{{end}}); + {{- end }} {{- end}} - {{- else if or (.Return.IsPrimitive) (eq .Return.KindType "enum") }} + {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return }}; - {{- else }} - {{javaReturn "" .Return }} returnedValue = {{Camel .Return.Schema.Module.Name}}TestHelper.makeTest{{Camel (javaType "" .Return )}}(); + {{- else if (eq .Return.KindType "extern") }} + {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .Return}}; + {{- else }} + {{javaReturn "" .Return }} returnedValue = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaDefault "" .Return}}{{end}}); {{- end }} @@ -375,11 +374,11 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- if .Return.IsPrimitive }} {{javaReturn "" .Return }} receivedByClient = resp_data.get{{ ( Camel (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result"{{if not .Return.IsArray}}, {{javaDefault "" .Return}}{{end}}); {{- else if .Return.IsArray }} - resp_data.setClassLoader({{Camel .Return.Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" .Return }} receivedByClient = {{Camel .Return.Type}}Parcelable.unwrapArray(({{Camel .Return.Type}}Parcelable[])resp_data.getParcelableArray("result", {{Camel .Return.Type}}Parcelable.class)); + resp_data.setClassLoader({{template "getParcelable" .Return }}.class.getClassLoader()); + {{javaReturn "" .Return }} receivedByClient = {{template "getParcelable" .Return }}.unwrapArray(({{template "getParcelable" .Return }}[])resp_data.getParcelableArray("result", {{template "getParcelable" .Return }}.class)); {{- else }} - resp_data.setClassLoader({{Camel .Return.Type}}Parcelable.class.getClassLoader()); - {{javaReturn "" .Return }} receivedByClient = resp_data.getParcelable("result", {{Camel .Return.Type}}Parcelable.class).get{{Camel (javaReturn "" .Return)}}(); + resp_data.setClassLoader({{template "getParcelable" .Return }}.class.getClassLoader()); + {{javaReturn "" .Return }} receivedByClient = resp_data.getParcelable("result", {{template "getParcelable" .Return }}.class).get{{Camel .Return.Type }}(); {{- end }} assertEquals(receivedByClient, returnedValue diff --git a/templates/api/abstract.java.tpl b/templates/api/abstract.java.tpl index 61a9469..f775029 100644 --- a/templates/api/abstract.java.tpl +++ b/templates/api/abstract.java.tpl @@ -2,7 +2,7 @@ package {{camel .Module.Name}}.{{camel .Module.Name}}_api; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; -//TODO imported/extern modules + {{- range .Module.Structs }} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; {{- end }} diff --git a/templates/api/additions.gradle.tpl b/templates/api/additions.gradle.tpl index e3f0f90..9bbb077 100644 --- a/templates/api/additions.gradle.tpl +++ b/templates/api/additions.gradle.tpl @@ -4,4 +4,13 @@ android { dependencies { implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + {{- range .Module.Externs}} + {{- $externInfo := javaExtern . }} + {{- if $externInfo.DownloadPackage}} + api '{{$externInfo.DownloadPackage}}{{ if $externInfo.Version}}:{{$externInfo.Version}}{{end}}' + {{- end }} + {{- end }} + {{- range .Module.Imports}} + api project(':{{camel .Name}}_api') + {{- end }} } diff --git a/templates/api/build.gradle.tpl b/templates/api/build.gradle.tpl index aa9593e..c0264cf 100644 --- a/templates/api/build.gradle.tpl +++ b/templates/api/build.gradle.tpl @@ -12,4 +12,13 @@ java { dependencies { implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + {{- range .Module.Externs}} + {{- $externInfo := javaExtern . }} + {{- if $externInfo.DownloadPackage}} + api '{{$externInfo.DownloadPackage}}{{ if $externInfo.Version}}:{{$externInfo.Version}}{{end}}' + {{- end }} + {{- end }} + {{- range .Module.Imports}} + api '{{camel .Name}}:{{camel .Name}}_api:{{ ($.System.LookupModule .Name).Version }}' + {{- end }} } \ No newline at end of file diff --git a/templates/api/testhelper.java.tpl b/templates/api/testhelper.java.tpl index 51d673e..c0dbe7b 100644 --- a/templates/api/testhelper.java.tpl +++ b/templates/api/testhelper.java.tpl @@ -4,6 +4,17 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; import java.util.Arrays; +{{- define "getMakeTestHelper"}} + {{- $ImportSchema:= printf "%s" ( camel .Schema.Import ) }} + {{- $TypeName := Camel .Type -}} + {{- if not (eq $ImportSchema "" ) -}} + {{- $ClassName := printf "%sTestHelper" ( Camel .Schema.Import ) -}} + {{$ImportSchema}}.{{$ImportSchema}}_api.{{$ClassName}}.makeTest{{$TypeName}} + {{- else -}} + {{- $ClassName := printf "%sTestHelper" ( Camel .Schema.Module.Name ) -}} + {{$ClassName}}.makeTest{{$TypeName}} + {{- end -}} +{{- end }} public class {{Camel .Module.Name}}TestHelper { @@ -18,17 +29,40 @@ public class {{Camel .Module.Name}}TestHelper {{- if or (.IsPrimitive) (eq .KindType "enum") }} testStruct.{{camel .Name}}[0] = {{ javaTestValue "" . }}; {{- else }} - testStruct.{{camel .Name}}[0] = makeTest{{Camel (javaElementType "" .) }}(); + testStruct.{{camel .Name}}[0] = {{template "getMakeTestHelper" . }}(); {{- end }} {{- else if or (.IsPrimitive) (eq .KindType "enum") }} testStruct.{{camel .Name}} = {{javaTestValue "" . }}; {{- else }} - testStruct.{{camel .Name}} = makeTest{{javaType "" . }}(); + testStruct.{{camel .Name}} = {{template "getMakeTestHelper" . }}(); {{- end}} {{- end }} return testStruct; } - {{- end}} +{{- range .Module.Interfaces}} + + static public I{{Camel .Name}} makeTest{{Camel .Name}}(I{{Camel .Name}} testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} +{{- range .Properties }} +{{- if .IsArray}} + {{javaReturn "" .}} local{{camel .Name}} = new {{javaElementType "" . }}[1]; + {{- if or (.IsPrimitive) (eq .KindType "enum") }} + local{{camel .Name}}[0] = {{ javaTestValue "" . }}; + {{- else if and (ne .KindType "extern") (ne .KindType "interface") }} + local{{camel .Name}}[0] = {{template "getMakeTestHelper" . }}(); + {{- end }} + testObjToFill.set{{Camel .Name}}(local{{camel .Name}}); +{{- else if or (.IsPrimitive) (eq .KindType "enum") }} + testObjToFill.set{{Camel .Name}}({{javaTestValue "" . }}); +{{- else if and (ne .KindType "extern") (ne .KindType "interface") }} + {{javaReturn "" .}} local{{camel .Name}} = {{template "getMakeTestHelper" . }}(); + testObjToFill.set{{Camel .Name}}(local{{camel .Name}}); +{{- end}} +{{- end }} + return testObjToFill; + } +{{- end}} } \ No newline at end of file diff --git a/templates/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl index df11153..7ea37df 100644 --- a/templates/jnibridge/client/jnibridgeclient.java.tpl +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -6,12 +6,61 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Nam import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel .Interface.Name }}Client; -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- range .Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range .Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} {{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} import android.content.Context; import android.os.Bundle; diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl index 44ca6ef..0db70af 100644 --- a/templates/jnibridge/service/jnibridgeservice.java.tpl +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -6,13 +6,61 @@ import android.util.Log; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- range .Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} - +{{- range .Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/templates/settings.gradle.tpl b/templates/settings.gradle.tpl index e8c38aa..171a37a 100644 --- a/templates/settings.gradle.tpl +++ b/templates/settings.gradle.tpl @@ -19,6 +19,8 @@ dependencyResolutionManagement { } } +{{- $features := .Features}} + rootProject.name = "{{camel .Module.Name}}" {{- if .Features.android }} include ':{{camel .Module.Name}}_android_service' diff --git a/templates/stub/additions.gradle.tpl b/templates/stub/additions.gradle.tpl index 5dea7e1..a037509 100644 --- a/templates/stub/additions.gradle.tpl +++ b/templates/stub/additions.gradle.tpl @@ -1,3 +1,4 @@ + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_impl' diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index 610a8fe..fd11683 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -6,12 +6,60 @@ import android.util.Log; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.Abstract{{Camel .Interface.Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range .Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- range .Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} {{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_impl.{{.}}Service; +{{- end}} import java.util.Map; diff --git a/templates/testclientapp/application.java.tpl b/templates/testclientapp/application.java.tpl index 8efdbb5..af12fac 100644 --- a/templates/testclientapp/application.java.tpl +++ b/templates/testclientapp/application.java.tpl @@ -11,20 +11,66 @@ import android.widget.TextView; //TODO for each interface there coudl be a tab? now only first one is added +{{- if len (.Module.Interfaces) }} {{- $Interface := (index .Module.Interfaces 0) }} import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel $Interface.Name }}Client; -//import message type and parcelabe types -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range $Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} + {{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} + {{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} + {{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range $Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- range $Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} {{- end }} - +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; import java.util.concurrent.CompletableFuture; @@ -253,3 +299,4 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ } } +{{- end }} \ No newline at end of file diff --git a/templates/testclientapp/build.gradle.tpl b/templates/testclientapp/build.gradle.tpl index 873a2d5..e310eb0 100644 --- a/templates/testclientapp/build.gradle.tpl +++ b/templates/testclientapp/build.gradle.tpl @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_client_example' compileSdk 35 diff --git a/templates/testserviceapp/application.java.tpl b/templates/testserviceapp/application.java.tpl index a56be85..0673265 100644 --- a/templates/testserviceapp/application.java.tpl +++ b/templates/testserviceapp/application.java.tpl @@ -12,6 +12,7 @@ import android.content.Intent; //TODO for each interface there coudl be a tab? now only first one is added +{{- if len (.Module.Interfaces) }} {{- $Interface := (index .Module.Interfaces 0) }} import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $Interface.Name }}ServiceAdapter; @@ -19,14 +20,62 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $In import {{camel .Module.Name}}.{{camel .Module.Name}}_android_service.{{Camel $Interface.Name }}ServiceStarter; //import message type and parcelabe types -{{- range .Module.Structs }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range $Interface.Properties }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} + {{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} + {{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} + {{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} {{- end }} -{{- range .Module.Enums }} -import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; -import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +{{- range $Interface.Operations }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} + {{- if and (and (not .Return.Schema.Import) (not .Return.IsPrimitive)) (not .Return.IsVoid) }} +{{- $type := Camel .Return.Type }} + {{- if eq .Return.KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} +{{- end }} +{{- range $Interface.Signals }} + {{- range .Params }} + {{- if and (not .Schema.Import) (not .IsPrimitive) }} +{{- $type := Camel .Type }} + {{- if eq .KindType "interface" }} +{{- $interfacesToImport = (appendList $typesToImport $type) }} + {{- else }} +{{- $typesToImport = (appendList $typesToImport $type) }} + {{- end }} + {{- end }} + {{- end }} {{- end }} +{{- $typesToImport = unique $typesToImport }} +{{- $interfacesToImport = unique $interfacesToImport }} +{{- range $typesToImport}} +import {{$module}}.{{$module}}_api.{{.}}; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} +{{- range $interfacesToImport}} +import {{$module}}.{{$module}}_api.I{{.}}; +import {{$module}}.{{$module}}_impl.{{.}}Service; +import {{$module}}.{{$module}}_android_messenger.{{.}}Parcelable; +{{- end}} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel $Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel $Interface.Name}}; @@ -222,3 +271,4 @@ public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{ } +{{- end}} diff --git a/templates/testserviceapp/build.gradle.tpl b/templates/testserviceapp/build.gradle.tpl index d93347e..733396a 100644 --- a/templates/testserviceapp/build.gradle.tpl +++ b/templates/testserviceapp/build.gradle.tpl @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + + android { namespace '{{camel .Module.Name}}.{{camel .Module.Name}}serviceexample' compileSdk 35 diff --git a/test-apis b/test-apis index b4edda1..4c1e51e 160000 --- a/test-apis +++ b/test-apis @@ -1 +1 @@ -Subproject commit b4edda143aa66c6d378f4cef1bf825060b4534db +Subproject commit 4c1e51ec75744be4271d5d03b8cfec57ec0836be From e12f805de30239dc6681868026b714910cb9f354 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:49:33 +0200 Subject: [PATCH 37/45] fix: add goldenmaster for imports, externs, ref if --- goldenmaster/build.gradle | 25 + .../counter_android_client/additions.gradle | 22 + .../counter_android_client/build.gradle | 37 + .../counter_android_client/CounterClient.java | 681 ++++++++++++++ .../CounterClientTest.java | 520 +++++++++++ .../additions.gradle | 20 + .../counter_android_messenger/build.gradle | 32 + .../CounterMessageType.java | 47 + .../CounterParcelable.java | 74 ++ .../counter_android_service/additions.gradle | 22 + .../counter_android_service/build.gradle | 38 + .../src/main/AndroidManifest.xml | 19 + .../CounterServiceAdapter.java | 490 ++++++++++ .../CounterServiceFactory.java | 81 ++ .../CounterServiceStarter.java | 42 + .../ICounterServiceFactory.java | 7 + .../CounterServiceAdapterTest.java | 540 +++++++++++ .../counter/counter_api/additions.gradle | 9 + goldenmaster/counter/counter_api/build.gradle | 17 + .../counter/counter_api/AbstractCounter.java | 59 ++ .../counter_api/CounterTestHelper.java | 22 + .../java/counter/counter_api/ICounter.java | 41 + .../counter_api/ICounterEventListener.java | 10 + .../counter_client_example/build.gradle | 44 + .../src/main/AndroidManifest.xml | 27 + .../CounterTestClientApp.java | 322 +++++++ .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../counter/counter_impl/additions.gradle | 19 + .../counter/counter_impl/build.gradle | 30 + .../counter/counter_impl/CounterService.java | 202 +++++ .../counterjniclient/CounterJniClient.java | 234 +++++ .../counterjniservice/CounterJniService.java | 201 +++++ .../CounterJniServiceFactory.java | 81 ++ .../CounterJniServiceStarter.java | 42 + .../counterserviceexample/build.gradle | 46 + .../src/main/AndroidManifest.xml | 29 + .../CounterTestServiceApp.java | 256 ++++++ .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/counter/gradle.properties | 21 + .../counter/gradle/libs.versions.toml | 27 + goldenmaster/counter/settings.gradle | 29 + .../additions.gradle | 20 + .../customTypes_android_client/build.gradle | 35 + .../additions.gradle | 18 + .../build.gradle | 30 + .../Vector3DParcelable.java | 69 ++ .../additions.gradle | 19 + .../customTypes_android_service/build.gradle | 35 + .../src/main/AndroidManifest.xml | 15 + .../customTypes_api/additions.gradle | 7 + .../customTypes/customTypes_api/build.gradle | 15 + .../CustomTypesTestHelper.java | 18 + .../customTypes/customTypes_api/Vector3D.java | 55 ++ .../customTypes_client_example/build.gradle | 44 + .../src/main/AndroidManifest.xml | 27 + .../CustomTypesTestClientApp.java | 13 + .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../customTypes_impl/additions.gradle | 19 + .../customTypes/customTypes_impl/build.gradle | 30 + .../customTypesserviceexample/build.gradle | 46 + .../src/main/AndroidManifest.xml | 25 + .../CustomTypesTestServiceApp.java | 14 + .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/customTypes/gradle.properties | 21 + .../customTypes/gradle/libs.versions.toml | 27 + goldenmaster/customTypes/settings.gradle | 29 + .../additions.gradle | 20 + .../externTypes_android_client/build.gradle | 35 + .../additions.gradle | 18 + .../build.gradle | 30 + .../MyVector3DParcelable.java | 66 ++ .../additions.gradle | 19 + .../externTypes_android_service/build.gradle | 35 + .../src/main/AndroidManifest.xml | 15 + .../externTypes_api/additions.gradle | 8 + .../externTypes/externTypes_api/build.gradle | 16 + .../ExternTypesTestHelper.java | 9 + .../externTypes_client_example/build.gradle | 44 + .../src/main/AndroidManifest.xml | 27 + .../ExternTypesTestClientApp.java | 13 + .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../externTypes_impl/additions.gradle | 19 + .../externTypes/externTypes_impl/build.gradle | 30 + .../externTypesserviceexample/build.gradle | 46 + .../src/main/AndroidManifest.xml | 25 + .../ExternTypesTestServiceApp.java | 14 + .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + goldenmaster/externTypes/gradle.properties | 21 + .../externTypes/gradle/libs.versions.toml | 27 + goldenmaster/externTypes/settings.gradle | 29 + .../goldenmaster_example/build.gradle | 25 + .../GoldenmasterMainActivity.java | 35 + goldenmaster/settings.gradle | 5 + .../tbEnum_android_client/additions.gradle | 1 + .../EnumInterfaceClient.java | 29 +- .../EnumInterfaceClientTest.java | 32 +- .../EnumInterfaceParcelable.java | 78 ++ .../tbEnum_android_service/additions.gradle | 4 +- .../tbEnum_android_service/build.gradle | 1 - .../EnumInterfaceServiceAdapter.java | 75 +- .../EnumInterfaceServiceAdapterTest.java | 22 +- .../tbEnum_api/AbstractEnumInterface.java | 1 - .../tbEnum/tbEnum_api/TbEnumTestHelper.java | 10 +- .../tbEnum/tbEnum_client_example/build.gradle | 3 + .../TbEnumTestClientApp.java | 5 +- .../tbEnum/tbEnum_impl/additions.gradle | 1 + .../EnumInterfaceJniClient.java | 4 + .../EnumInterfaceJniService.java | 5 +- .../tbEnum/tbEnumserviceexample/build.gradle | 4 + goldenmaster/tbIfaceimport/gradle.properties | 21 + .../tbIfaceimport/gradle/libs.versions.toml | 27 + goldenmaster/tbIfaceimport/settings.gradle | 29 + .../additions.gradle | 20 + .../tbIfaceimport_android_client/build.gradle | 35 + .../EmptyIfClient.java | 205 +++++ .../EmptyIfClientTest.java | 146 +++ .../additions.gradle | 18 + .../build.gradle | 30 + .../EmptyIfMessageType.java | 30 + .../EmptyIfParcelable.java | 62 ++ .../additions.gradle | 20 + .../build.gradle | 36 + .../src/main/AndroidManifest.xml | 19 + .../EmptyIfServiceAdapter.java | 253 ++++++ .../EmptyIfServiceFactory.java | 81 ++ .../EmptyIfServiceStarter.java | 42 + .../IEmptyIfServiceFactory.java | 7 + .../EmptyIfServiceAdapterTest.java | 199 ++++ .../tbIfaceimport_api/additions.gradle | 7 + .../tbIfaceimport_api/build.gradle | 15 + .../tbIfaceimport_api/AbstractEmptyIf.java | 24 + .../tbIfaceimport_api/IEmptyIf.java | 16 + .../IEmptyIfEventListener.java | 5 + .../TbIfaceimportTestHelper.java | 15 + .../tbIfaceimport_client_example/build.gradle | 44 + .../src/main/AndroidManifest.xml | 27 + .../TbIfaceimportTestClientApp.java | 187 ++++ .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbIfaceimport_impl/additions.gradle | 19 + .../tbIfaceimport_impl/build.gradle | 30 + .../tbIfaceimport_impl/EmptyIfService.java | 41 + .../EmptyIfJniClient.java | 71 ++ .../EmptyIfJniService.java | 48 + .../EmptyIfJniServiceFactory.java | 81 ++ .../EmptyIfJniServiceStarter.java | 42 + .../tbIfaceimportserviceexample/build.gradle | 46 + .../src/main/AndroidManifest.xml | 29 + .../TbIfaceimportTestServiceApp.java | 168 ++++ .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbNames_android_client/additions.gradle | 1 + .../tbNames_android_client/NamEsClient.java | 58 ++ .../NamEsClientTest.java | 54 +- .../EnumWithUnderScoresParcelable.java | 69 ++ .../NamEsMessageType.java | 14 +- .../NamEsParcelable.java | 72 ++ .../tbNames_android_service/additions.gradle | 4 +- .../tbNames_android_service/build.gradle | 1 - .../NamEsServiceAdapter.java | 91 +- .../NamEsServiceAdapterTest.java | 49 +- .../tbNames/tbNames_api/AbstractNamEs.java | 9 +- .../tbNames_api/EnumWithUnderScores.java | 30 + .../main/java/tbNames/tbNames_api/INamEs.java | 5 + .../tbNames_api/INamEsEventListener.java | 2 + .../tbNames_api/TbNamesTestHelper.java | 10 +- .../tbNames_client_example/build.gradle | 3 + .../TbNamesTestClientApp.java | 25 +- .../tbNames/tbNames_impl/additions.gradle | 1 + .../tbNames/tbNames_impl/NamEsService.java | 27 + .../tbNamesjniclient/NamEsJniClient.java | 22 + .../tbNamesjniservice/NamEsJniService.java | 26 +- .../tbNamesserviceexample/build.gradle | 4 + .../TbNamesTestServiceApp.java | 19 + goldenmaster/tbRefIfaces/gradle.properties | 21 + .../tbRefIfaces/gradle/libs.versions.toml | 27 + goldenmaster/tbRefIfaces/settings.gradle | 29 + .../additions.gradle | 21 + .../tbRefIfaces_android_client/build.gradle | 36 + .../ParentIfClient.java | 721 +++++++++++++++ .../SimpleLocalIfClient.java | 329 +++++++ .../ParentIfClientTest.java | 572 ++++++++++++ .../SimpleLocalIfClientTest.java | 241 +++++ .../additions.gradle | 19 + .../build.gradle | 31 + .../ParentIfMessageType.java | 50 ++ .../ParentIfParcelable.java | 75 ++ .../SimpleLocalIfMessageType.java | 35 + .../SimpleLocalIfParcelable.java | 64 ++ .../additions.gradle | 22 + .../tbRefIfaces_android_service/build.gradle | 38 + .../src/main/AndroidManifest.xml | 23 + .../IParentIfServiceFactory.java | 7 + .../ISimpleLocalIfServiceFactory.java | 7 + .../ParentIfServiceAdapter.java | 517 +++++++++++ .../ParentIfServiceFactory.java | 81 ++ .../ParentIfServiceStarter.java | 42 + .../SimpleLocalIfServiceAdapter.java | 316 +++++++ .../SimpleLocalIfServiceFactory.java | 81 ++ .../SimpleLocalIfServiceStarter.java | 42 + .../ParentIfServiceAdapterTest.java | 586 ++++++++++++ .../SimpleLocalIfServiceAdapterTest.java | 285 ++++++ .../tbRefIfaces_api/additions.gradle | 8 + .../tbRefIfaces/tbRefIfaces_api/build.gradle | 16 + .../tbRefIfaces_api/AbstractParentIf.java | 80 ++ .../AbstractSimpleLocalIf.java | 38 + .../tbRefIfaces_api/IParentIf.java | 44 + .../IParentIfEventListener.java | 13 + .../tbRefIfaces_api/ISimpleLocalIf.java | 23 + .../ISimpleLocalIfEventListener.java | 7 + .../TbRefIfacesTestHelper.java | 26 + .../tbRefIfaces_client_example/build.gradle | 44 + .../src/main/AndroidManifest.xml | 27 + .../TbRefIfacesTestClientApp.java | 227 +++++ .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbRefIfaces_impl/additions.gradle | 19 + .../tbRefIfaces/tbRefIfaces_impl/build.gradle | 30 + .../tbRefIfaces_impl/ParentIfService.java | 219 +++++ .../SimpleLocalIfService.java | 85 ++ .../ParentIfJniClient.java | 257 ++++++ .../SimpleLocalIfJniClient.java | 117 +++ .../ParentIfJniService.java | 218 +++++ .../ParentIfJniServiceFactory.java | 81 ++ .../ParentIfJniServiceStarter.java | 42 + .../SimpleLocalIfJniService.java | 90 ++ .../SimpleLocalIfJniServiceFactory.java | 81 ++ .../SimpleLocalIfJniServiceStarter.java | 42 + .../tbRefIfacesserviceexample/build.gradle | 46 + .../src/main/AndroidManifest.xml | 33 + .../TbRefIfacesTestServiceApp.java | 202 +++++ .../res/drawable/ic_launcher_background.xml | 170 ++++ .../res/drawable/ic_launcher_foreground.xml | 30 + .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values-night/themes.xml | 16 + .../src/main/res/values/colors.xml | 10 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 16 + .../src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../tbSame1_android_client/additions.gradle | 1 + .../SameEnum1InterfaceClient.java | 14 +- .../SameEnum2InterfaceClient.java | 21 +- .../SameStruct1InterfaceClient.java | 14 +- .../SameStruct2InterfaceClient.java | 21 +- .../SameEnum1InterfaceClientTest.java | 8 +- .../SameEnum2InterfaceClientTest.java | 17 +- .../SameStruct1InterfaceClientTest.java | 8 +- .../SameStruct2InterfaceClientTest.java | 17 +- .../SameEnum1InterfaceParcelable.java | 66 ++ .../SameEnum2InterfaceParcelable.java | 70 ++ .../SameStruct1InterfaceParcelable.java | 66 ++ .../SameStruct2InterfaceParcelable.java | 70 ++ .../tbSame1_android_service/additions.gradle | 4 +- .../tbSame1_android_service/build.gradle | 1 - .../SameEnum1InterfaceServiceAdapter.java | 72 +- .../SameEnum2InterfaceServiceAdapter.java | 74 +- .../SameStruct1InterfaceServiceAdapter.java | 72 +- .../SameStruct2InterfaceServiceAdapter.java | 74 +- .../SameEnum1InterfaceServiceAdapterTest.java | 7 +- .../SameEnum2InterfaceServiceAdapterTest.java | 13 +- ...ameStruct1InterfaceServiceAdapterTest.java | 7 +- ...ameStruct2InterfaceServiceAdapterTest.java | 13 +- .../AbstractSameEnum1Interface.java | 1 - .../AbstractSameEnum2Interface.java | 1 - .../AbstractSameStruct1Interface.java | 1 - .../AbstractSameStruct2Interface.java | 1 - .../tbSame1_api/TbSame1TestHelper.java | 33 +- .../tbSame1_client_example/build.gradle | 3 + .../TbSame1TestClientApp.java | 11 +- .../tbSame1/tbSame1_impl/additions.gradle | 1 + .../SameEnum1InterfaceService.java | 3 - .../SameEnum2InterfaceService.java | 2 - .../SameStruct1InterfaceService.java | 3 - .../SameStruct2InterfaceService.java | 2 - .../SameEnum1InterfaceJniClient.java | 4 +- .../SameEnum2InterfaceJniClient.java | 4 +- .../SameStruct1InterfaceJniClient.java | 4 +- .../SameStruct2InterfaceJniClient.java | 4 +- .../SameEnum1InterfaceJniService.java | 5 +- .../SameEnum2InterfaceJniService.java | 5 +- .../SameStruct1InterfaceJniService.java | 5 +- .../SameStruct2InterfaceJniService.java | 5 +- .../tbSame1serviceexample/build.gradle | 4 + .../TbSame1TestServiceApp.java | 6 - .../tbSame2_android_client/additions.gradle | 1 + .../SameEnum1InterfaceClient.java | 14 +- .../SameEnum2InterfaceClient.java | 21 +- .../SameStruct1InterfaceClient.java | 14 +- .../SameStruct2InterfaceClient.java | 21 +- .../SameEnum1InterfaceClientTest.java | 8 +- .../SameEnum2InterfaceClientTest.java | 17 +- .../SameStruct1InterfaceClientTest.java | 8 +- .../SameStruct2InterfaceClientTest.java | 17 +- .../SameEnum1InterfaceParcelable.java | 66 ++ .../SameEnum2InterfaceParcelable.java | 70 ++ .../SameStruct1InterfaceParcelable.java | 66 ++ .../SameStruct2InterfaceParcelable.java | 70 ++ .../tbSame2_android_service/additions.gradle | 4 +- .../tbSame2_android_service/build.gradle | 1 - .../SameEnum1InterfaceServiceAdapter.java | 72 +- .../SameEnum2InterfaceServiceAdapter.java | 74 +- .../SameStruct1InterfaceServiceAdapter.java | 72 +- .../SameStruct2InterfaceServiceAdapter.java | 74 +- .../SameEnum1InterfaceServiceAdapterTest.java | 7 +- .../SameEnum2InterfaceServiceAdapterTest.java | 13 +- ...ameStruct1InterfaceServiceAdapterTest.java | 7 +- ...ameStruct2InterfaceServiceAdapterTest.java | 13 +- .../AbstractSameEnum1Interface.java | 1 - .../AbstractSameEnum2Interface.java | 1 - .../AbstractSameStruct1Interface.java | 1 - .../AbstractSameStruct2Interface.java | 1 - .../tbSame2_api/TbSame2TestHelper.java | 33 +- .../tbSame2_client_example/build.gradle | 3 + .../TbSame2TestClientApp.java | 11 +- .../tbSame2/tbSame2_impl/additions.gradle | 1 + .../SameEnum1InterfaceService.java | 3 - .../SameEnum2InterfaceService.java | 2 - .../SameStruct1InterfaceService.java | 3 - .../SameStruct2InterfaceService.java | 2 - .../SameEnum1InterfaceJniClient.java | 4 +- .../SameEnum2InterfaceJniClient.java | 4 +- .../SameStruct1InterfaceJniClient.java | 4 +- .../SameStruct2InterfaceJniClient.java | 4 +- .../SameEnum1InterfaceJniService.java | 5 +- .../SameEnum2InterfaceJniService.java | 5 +- .../SameStruct1InterfaceJniService.java | 5 +- .../SameStruct2InterfaceJniService.java | 5 +- .../tbSame2serviceexample/build.gradle | 4 + .../TbSame2TestServiceApp.java | 6 - .../tbSimple_android_client/additions.gradle | 1 + .../NoOperationsInterfaceClient.java | 2 + .../NoPropertiesInterfaceClient.java | 4 + .../NoSignalsInterfaceClient.java | 2 + .../SimpleArrayInterfaceClient.java | 16 + .../SimpleInterfaceClient.java | 17 + .../VoidInterfaceClient.java | 2 + .../NoOperationsInterfaceClientTest.java | 10 +- .../NoPropertiesInterfaceClientTest.java | 2 + .../NoSignalsInterfaceClientTest.java | 12 +- .../SimpleArrayInterfaceClientTest.java | 67 +- .../SimpleInterfaceClientTest.java | 49 +- .../VoidInterfaceClientTest.java | 1 + .../EmptyInterfaceParcelable.java | 62 ++ .../NoOperationsInterfaceParcelable.java | 66 ++ .../NoPropertiesInterfaceParcelable.java | 62 ++ .../NoSignalsInterfaceParcelable.java | 66 ++ .../SimpleArrayInterfaceParcelable.java | 80 ++ .../SimpleInterfaceParcelable.java | 78 ++ .../VoidInterfaceParcelable.java | 62 ++ .../tbSimple_android_service/additions.gradle | 4 +- .../tbSimple_android_service/build.gradle | 1 - .../EmptyInterfaceServiceAdapter.java | 63 +- .../NoOperationsInterfaceServiceAdapter.java | 63 +- .../NoPropertiesInterfaceServiceAdapter.java | 65 +- .../NoSignalsInterfaceServiceAdapter.java | 65 +- .../SimpleArrayInterfaceServiceAdapter.java | 71 +- .../SimpleInterfaceServiceAdapter.java | 72 +- .../VoidInterfaceServiceAdapter.java | 64 +- .../EmptyInterfaceServiceAdapterTest.java | 1 + ...OperationsInterfaceServiceAdapterTest.java | 5 +- ...PropertiesInterfaceServiceAdapterTest.java | 3 + .../NoSignalsInterfaceServiceAdapterTest.java | 3 +- ...impleArrayInterfaceServiceAdapterTest.java | 32 +- .../SimpleInterfaceServiceAdapterTest.java | 17 +- .../VoidInterfaceServiceAdapterTest.java | 2 + .../tbSimple_api/AbstractEmptyInterface.java | 1 - .../AbstractNoOperationsInterface.java | 1 - .../AbstractNoPropertiesInterface.java | 1 - .../AbstractNoSignalsInterface.java | 1 - .../AbstractSimpleArrayInterface.java | 1 - .../tbSimple_api/AbstractSimpleInterface.java | 1 - .../tbSimple_api/AbstractVoidInterface.java | 1 - .../tbSimple_api/TbSimpleTestHelper.java | 79 +- .../tbSimple_client_example/build.gradle | 3 + .../TbSimpleTestClientApp.java | 5 +- .../tbSimple/tbSimple_impl/additions.gradle | 1 + .../EmptyInterfaceJniService.java | 1 - .../NoOperationsInterfaceJniService.java | 1 - .../NoPropertiesInterfaceJniService.java | 1 - .../NoSignalsInterfaceJniService.java | 1 - .../SimpleArrayInterfaceJniService.java | 1 - .../SimpleInterfaceJniService.java | 1 - .../VoidInterfaceJniService.java | 1 - .../tbSimpleserviceexample/build.gradle | 4 + .../testbed1_android_client/additions.gradle | 1 + .../StructArray2InterfaceClient.java | 846 ++++++++++++++++++ .../StructArrayInterfaceClient.java | 162 +++- .../StructInterfaceClient.java | 33 +- .../StructArray2InterfaceClientTest.java | 646 +++++++++++++ .../StructArrayInterfaceClientTest.java | 153 +++- .../StructInterfaceClientTest.java | 50 +- .../Enum0Parcelable.java | 69 ++ .../StructArray2InterfaceMessageType.java | 54 ++ .../StructArray2InterfaceParcelable.java | 87 ++ .../StructArrayInterfaceMessageType.java | 29 +- .../StructArrayInterfaceParcelable.java | 82 ++ .../StructBoolWithArrayParcelable.java | 65 ++ .../StructEnumParcelable.java | 67 ++ .../StructEnumWithArrayParcelable.java | 67 ++ .../StructFloatWithArrayParcelable.java | 65 ++ .../StructIntWithArrayParcelable.java | 65 ++ .../StructInterfaceParcelable.java | 78 ++ .../StructStringWithArrayParcelable.java | 65 ++ .../StructStructParcelable.java | 67 ++ .../StructStructWithArrayParcelable.java | 67 ++ .../testbed1_android_service/additions.gradle | 4 +- .../testbed1_android_service/build.gradle | 1 - .../src/main/AndroidManifest.xml | 4 + .../IStructArray2InterfaceServiceFactory.java | 7 + .../StructArray2InterfaceServiceAdapter.java | 587 ++++++++++++ .../StructArray2InterfaceServiceFactory.java | 81 ++ .../StructArray2InterfaceServiceStarter.java | 42 + .../StructArrayInterfaceServiceAdapter.java | 146 ++- .../StructInterfaceServiceAdapter.java | 79 +- ...ructArray2InterfaceServiceAdapterTest.java | 677 ++++++++++++++ ...tructArrayInterfaceServiceAdapterTest.java | 140 ++- .../StructInterfaceServiceAdapterTest.java | 40 +- .../AbstractStructArray2Interface.java | 100 +++ .../AbstractStructArrayInterface.java | 24 +- .../testbed1_api/AbstractStructInterface.java | 10 +- .../java/testbed1/testbed1_api/Enum0.java | 30 + .../testbed1_api/IStructArray2Interface.java | 63 ++ .../IStructArray2InterfaceEventListener.java | 27 + .../testbed1_api/IStructArrayInterface.java | 16 + .../IStructArrayInterfaceEventListener.java | 11 + .../testbed1_api/IStructInterface.java | 9 + .../IStructInterfaceEventListener.java | 9 + .../testbed1_api/StructBoolWithArray.java | 44 + .../testbed1/testbed1_api/StructEnum.java | 44 + .../testbed1_api/StructEnumWithArray.java | 44 + .../testbed1_api/StructFloatWithArray.java | 44 + .../testbed1_api/StructIntWithArray.java | 44 + .../testbed1_api/StructStringWithArray.java | 44 + .../testbed1/testbed1_api/StructStruct.java | 44 + .../testbed1_api/StructStructWithArray.java | 48 + .../testbed1_api/Testbed1TestHelper.java | 113 ++- .../testbed1_client_example/build.gradle | 3 + .../Testbed1TestClientApp.java | 9 +- .../testbed1/testbed1_impl/additions.gradle | 1 + .../StructArray2InterfaceService.java | 266 ++++++ .../StructArrayInterfaceService.java | 47 +- .../testbed1_impl/StructInterfaceService.java | 2 +- .../StructArray2InterfaceJniClient.java | 314 +++++++ .../StructArrayInterfaceJniClient.java | 54 +- .../StructInterfaceJniClient.java | 6 +- .../StructArray2InterfaceJniService.java | 273 ++++++ ...tructArray2InterfaceJniServiceFactory.java | 81 ++ ...tructArray2InterfaceJniServiceStarter.java | 42 + .../StructArrayInterfaceJniService.java | 51 +- .../StructInterfaceJniService.java | 7 +- .../testbed1serviceexample/build.gradle | 4 + .../src/main/AndroidManifest.xml | 4 + .../Testbed1TestServiceApp.java | 4 +- .../testbed2_android_client/additions.gradle | 1 + .../ManyParamInterfaceClient.java | 28 +- .../NestedStruct1InterfaceClient.java | 26 +- .../NestedStruct2InterfaceClient.java | 33 +- .../NestedStruct3InterfaceClient.java | 42 +- .../ManyParamInterfaceClientTest.java | 24 +- .../NestedStruct1InterfaceClientTest.java | 8 +- .../NestedStruct2InterfaceClientTest.java | 17 +- .../NestedStruct3InterfaceClientTest.java | 27 +- .../ManyParamInterfaceParcelable.java | 70 ++ .../NestedStruct1InterfaceParcelable.java | 66 ++ .../NestedStruct2InterfaceParcelable.java | 70 ++ .../NestedStruct3InterfaceParcelable.java | 74 ++ .../NestedStruct3Parcelable.java | 14 +- .../testbed2_android_service/additions.gradle | 4 +- .../testbed2_android_service/build.gradle | 1 - .../ManyParamInterfaceServiceAdapter.java | 87 +- .../NestedStruct1InterfaceServiceAdapter.java | 84 +- .../NestedStruct2InterfaceServiceAdapter.java | 86 +- .../NestedStruct3InterfaceServiceAdapter.java | 89 +- .../ManyParamInterfaceServiceAdapterTest.java | 9 +- ...tedStruct1InterfaceServiceAdapterTest.java | 7 +- ...tedStruct2InterfaceServiceAdapterTest.java | 13 +- ...tedStruct3InterfaceServiceAdapterTest.java | 20 +- .../AbstractManyParamInterface.java | 1 - .../AbstractNestedStruct1Interface.java | 1 - .../AbstractNestedStruct2Interface.java | 1 - .../AbstractNestedStruct3Interface.java | 1 - .../testbed2/testbed2_api/NestedStruct3.java | 41 +- .../testbed2_api/Testbed2TestHelper.java | 58 +- .../testbed2_client_example/build.gradle | 3 + .../Testbed2TestClientApp.java | 25 +- .../testbed2/testbed2_impl/additions.gradle | 1 + .../ManyParamInterfaceService.java | 10 - .../NestedStruct1InterfaceService.java | 9 - .../NestedStruct2InterfaceService.java | 8 - .../NestedStruct3InterfaceService.java | 7 - .../ManyParamInterfaceJniClient.java | 10 - .../NestedStruct1InterfaceJniClient.java | 10 +- .../NestedStruct2InterfaceJniClient.java | 10 +- .../NestedStruct3InterfaceJniClient.java | 10 +- .../ManyParamInterfaceJniService.java | 11 - .../NestedStruct1InterfaceJniService.java | 11 +- .../NestedStruct2InterfaceJniService.java | 11 +- .../NestedStruct3InterfaceJniService.java | 11 +- .../testbed2serviceexample/build.gradle | 4 + .../Testbed2TestServiceApp.java | 20 - 680 files changed, 27239 insertions(+), 1587 deletions(-) create mode 100644 goldenmaster/counter/counter_android_client/additions.gradle create mode 100644 goldenmaster/counter/counter_android_client/build.gradle create mode 100644 goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java create mode 100644 goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java create mode 100644 goldenmaster/counter/counter_android_messenger/additions.gradle create mode 100644 goldenmaster/counter/counter_android_messenger/build.gradle create mode 100644 goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterMessageType.java create mode 100644 goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterParcelable.java create mode 100644 goldenmaster/counter/counter_android_service/additions.gradle create mode 100644 goldenmaster/counter/counter_android_service/build.gradle create mode 100644 goldenmaster/counter/counter_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java create mode 100644 goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java create mode 100644 goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java create mode 100644 goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/ICounterServiceFactory.java create mode 100644 goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java create mode 100644 goldenmaster/counter/counter_api/additions.gradle create mode 100644 goldenmaster/counter/counter_api/build.gradle create mode 100644 goldenmaster/counter/counter_api/src/main/java/counter/counter_api/AbstractCounter.java create mode 100644 goldenmaster/counter/counter_api/src/main/java/counter/counter_api/CounterTestHelper.java create mode 100644 goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounter.java create mode 100644 goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounterEventListener.java create mode 100644 goldenmaster/counter/counter_client_example/build.gradle create mode 100644 goldenmaster/counter/counter_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/counter/counter_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/counter/counter_impl/additions.gradle create mode 100644 goldenmaster/counter/counter_impl/build.gradle create mode 100644 goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java create mode 100644 goldenmaster/counter/counterjniclient/CounterJniClient.java create mode 100644 goldenmaster/counter/counterjniservice/CounterJniService.java create mode 100644 goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java create mode 100644 goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java create mode 100644 goldenmaster/counter/counterserviceexample/build.gradle create mode 100644 goldenmaster/counter/counterserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/counter/counterserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/counter/gradle.properties create mode 100644 goldenmaster/counter/gradle/libs.versions.toml create mode 100644 goldenmaster/counter/settings.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_client/additions.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_client/build.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_messenger/additions.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_messenger/build.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_messenger/src/main/java/customTypes/customTypes_android_messenger/Vector3DParcelable.java create mode 100644 goldenmaster/customTypes/customTypes_android_service/additions.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_service/build.gradle create mode 100644 goldenmaster/customTypes/customTypes_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/customTypes/customTypes_api/additions.gradle create mode 100644 goldenmaster/customTypes/customTypes_api/build.gradle create mode 100644 goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/CustomTypesTestHelper.java create mode 100644 goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/Vector3D.java create mode 100644 goldenmaster/customTypes/customTypes_client_example/build.gradle create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/java/customTypes/customTypes_client_example/CustomTypesTestClientApp.java create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/customTypes/customTypes_impl/additions.gradle create mode 100644 goldenmaster/customTypes/customTypes_impl/build.gradle create mode 100644 goldenmaster/customTypes/customTypesserviceexample/build.gradle create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/java/customTypes/customTypesserviceexample/CustomTypesTestServiceApp.java create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/customTypes/gradle.properties create mode 100644 goldenmaster/customTypes/gradle/libs.versions.toml create mode 100644 goldenmaster/customTypes/settings.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_client/additions.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_client/build.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_messenger/additions.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_messenger/build.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_messenger/src/main/java/externTypes/externTypes_android_messenger/MyVector3DParcelable.java create mode 100644 goldenmaster/externTypes/externTypes_android_service/additions.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_service/build.gradle create mode 100644 goldenmaster/externTypes/externTypes_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/externTypes/externTypes_api/additions.gradle create mode 100644 goldenmaster/externTypes/externTypes_api/build.gradle create mode 100644 goldenmaster/externTypes/externTypes_api/src/main/java/externTypes/externTypes_api/ExternTypesTestHelper.java create mode 100644 goldenmaster/externTypes/externTypes_client_example/build.gradle create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/java/externTypes/externTypes_client_example/ExternTypesTestClientApp.java create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/externTypes/externTypes_impl/additions.gradle create mode 100644 goldenmaster/externTypes/externTypes_impl/build.gradle create mode 100644 goldenmaster/externTypes/externTypesserviceexample/build.gradle create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/java/externTypes/externTypesserviceexample/ExternTypesTestServiceApp.java create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/externTypes/gradle.properties create mode 100644 goldenmaster/externTypes/gradle/libs.versions.toml create mode 100644 goldenmaster/externTypes/settings.gradle create mode 100644 goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceParcelable.java create mode 100644 goldenmaster/tbIfaceimport/gradle.properties create mode 100644 goldenmaster/tbIfaceimport/gradle/libs.versions.toml create mode 100644 goldenmaster/tbIfaceimport/settings.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_client/additions.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/additions.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfMessageType.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfParcelable.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/additions.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/IEmptyIfServiceFactory.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_api/additions.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_api/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/AbstractEmptyIf.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIf.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIfEventListener.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/TbIfaceimportTestHelper.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_impl/additions.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_impl/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/build.gradle create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/EnumWithUnderScoresParcelable.java create mode 100644 goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsParcelable.java create mode 100644 goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/EnumWithUnderScores.java create mode 100644 goldenmaster/tbRefIfaces/gradle.properties create mode 100644 goldenmaster/tbRefIfaces/gradle/libs.versions.toml create mode 100644 goldenmaster/tbRefIfaces/settings.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_client/additions.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/additions.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfMessageType.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfParcelable.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfMessageType.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfParcelable.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/additions.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/IParentIfServiceFactory.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ISimpleLocalIfServiceFactory.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/additions.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractParentIf.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractSimpleLocalIf.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIf.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIfEventListener.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIf.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIfEventListener.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/TbRefIfacesTestHelper.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_impl/additions.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_impl/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/build.gradle create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/AndroidManifest.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values-night/themes.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/colors.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/strings.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/themes.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/backup_rules.xml create mode 100644 goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/data_extraction_rules.xml create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceParcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceParcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceParcelable.java create mode 100644 goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceParcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceParcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceParcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceParcelable.java create mode 100644 goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceParcelable.java create mode 100644 goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java create mode 100644 goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/Enum0Parcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceMessageType.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolWithArrayParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumWithArrayParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatWithArrayParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntWithArrayParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringWithArrayParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructWithArrayParcelable.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArray2InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java create mode 100644 goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArray2Interface.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Enum0.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2Interface.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2InterfaceEventListener.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBoolWithArray.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnum.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnumWithArray.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloatWithArray.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructIntWithArray.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStringWithArray.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStruct.java create mode 100644 goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStructWithArray.java create mode 100644 goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java create mode 100644 goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java create mode 100644 goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceParcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceParcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceParcelable.java create mode 100644 goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceParcelable.java diff --git a/goldenmaster/build.gradle b/goldenmaster/build.gradle index c5765ed..c1b914b 100644 --- a/goldenmaster/build.gradle +++ b/goldenmaster/build.gradle @@ -46,6 +46,31 @@ tasks.register('runAll') { dependsOn gradle.includedBuild('testbed1').task(':testbed1_android_messenger:build') dependsOn gradle.includedBuild('testbed1').task(':testbed1_impl:build') dependsOn gradle.includedBuild('testbed1').task(':testbed1_api:build') + dependsOn gradle.includedBuild('customTypes').task(':customTypes_android_service:build') + dependsOn gradle.includedBuild('customTypes').task(':customTypes_android_client:build') + dependsOn gradle.includedBuild('customTypes').task(':customTypes_android_messenger:build') + dependsOn gradle.includedBuild('customTypes').task(':customTypes_impl:build') + dependsOn gradle.includedBuild('customTypes').task(':customTypes_api:build') + dependsOn gradle.includedBuild('externTypes').task(':externTypes_android_service:build') + dependsOn gradle.includedBuild('externTypes').task(':externTypes_android_client:build') + dependsOn gradle.includedBuild('externTypes').task(':externTypes_android_messenger:build') + dependsOn gradle.includedBuild('externTypes').task(':externTypes_impl:build') + dependsOn gradle.includedBuild('externTypes').task(':externTypes_api:build') + dependsOn gradle.includedBuild('counter').task(':counter_android_service:build') + dependsOn gradle.includedBuild('counter').task(':counter_android_client:build') + dependsOn gradle.includedBuild('counter').task(':counter_android_messenger:build') + dependsOn gradle.includedBuild('counter').task(':counter_impl:build') + dependsOn gradle.includedBuild('counter').task(':counter_api:build') + dependsOn gradle.includedBuild('tbIfaceimport').task(':tbIfaceimport_android_service:build') + dependsOn gradle.includedBuild('tbIfaceimport').task(':tbIfaceimport_android_client:build') + dependsOn gradle.includedBuild('tbIfaceimport').task(':tbIfaceimport_android_messenger:build') + dependsOn gradle.includedBuild('tbIfaceimport').task(':tbIfaceimport_impl:build') + dependsOn gradle.includedBuild('tbIfaceimport').task(':tbIfaceimport_api:build') + dependsOn gradle.includedBuild('tbRefIfaces').task(':tbRefIfaces_android_service:build') + dependsOn gradle.includedBuild('tbRefIfaces').task(':tbRefIfaces_android_client:build') + dependsOn gradle.includedBuild('tbRefIfaces').task(':tbRefIfaces_android_messenger:build') + dependsOn gradle.includedBuild('tbRefIfaces').task(':tbRefIfaces_impl:build') + dependsOn gradle.includedBuild('tbRefIfaces').task(':tbRefIfaces_api:build') dependsOn project(':goldenmaster_example').tasks.named('build') diff --git a/goldenmaster/counter/counter_android_client/additions.gradle b/goldenmaster/counter/counter_android_client/additions.gradle new file mode 100644 index 0000000..1693474 --- /dev/null +++ b/goldenmaster/counter/counter_android_client/additions.gradle @@ -0,0 +1,22 @@ +android { + namespace 'counter.counter_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + implementation project(':counter_android_messenger') + api project(':customTypes_android_messenger') + api project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_android_client/build.gradle b/goldenmaster/counter/counter_android_client/build.gradle new file mode 100644 index 0000000..857ee26 --- /dev/null +++ b/goldenmaster/counter/counter_android_client/build.gradle @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "counter" +version = "1.0.0" + +android { + namespace 'counter.counter_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + implementation project(':counter_android_messenger') + api 'customTypes:customTypes_android_messenger:1.0.0' + api 'externTypes:externTypes_android_messenger:1.0.0' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java new file mode 100644 index 0000000..fce0500 --- /dev/null +++ b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java @@ -0,0 +1,681 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package counter.counter_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import counter.counter_api.ICounterEventListener; +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_android_messenger.CounterMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class CounterClient extends AbstractCounter implements ServiceConnection +{ + private static final String TAG = "CounterClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private customTypes.customTypes_api.Vector3D m_vector = new customTypes.customTypes_api.Vector3D(); + private org.apache.commons.math3.geometry.euclidean.threed.Vector3D m_extern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + private customTypes.customTypes_api.Vector3D[] m_vectorArray = new customTypes.customTypes_api.Vector3D[]{}; + private org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] m_extern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[]{}; + + + public CounterClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type CounterServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "counter.counter_android_service.CounterServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, CounterMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, CounterMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (CounterMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + + customTypes.customTypes_api.Vector3D vector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + onVector(vector); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + onExternVector(extern_vector); + + customTypes.customTypes_api.Vector3D[] vectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + onVectorArray(vectorArray); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + onExternVectorArray(extern_vectorArray); + + break; + } + case SET_Vector: + { + Bundle data = msg.getData(); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + + customTypes.customTypes_api.Vector3D vector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + + onVector(vector); + break; + } + case SET_ExternVector: + { + Bundle data = msg.getData(); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + + onExternVector(extern_vector); + break; + } + case SET_VectorArray: + { + Bundle data = msg.getData(); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + + customTypes.customTypes_api.Vector3D[] vectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + + onVectorArray(vectorArray); + break; + } + case SET_ExternVectorArray: + { + Bundle data = msg.getData(); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + + onExternVectorArray(extern_vectorArray); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_ValueChanged: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D vector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + + customTypes.customTypes_api.Vector3D[] vectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + onValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + break; + } + case RPC_IncrementResp: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received CounterMessageType.RPC_IncrementResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_IncrementArrayResp: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received CounterMessageType.RPC_IncrementArrayResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_DecrementResp: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received CounterMessageType.RPC_DecrementResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_DecrementArrayResp: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received CounterMessageType.RPC_DecrementArrayResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setVector(customTypes.customTypes_api.Vector3D vector) + { + Log.i(TAG, "request setVector called "+ vector); + if (! m_vector.equals(vector)) + { + Message msg = new Message(); + msg.what = CounterMessageType.PROP_Vector.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(vector)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onVector(customTypes.customTypes_api.Vector3D vector) + { + Log.i(TAG, "value received from service for Vector "); + if (! m_vector.equals(vector)) + { + m_vector = vector; + fireVectorChanged(vector); + } + + } + + @Override + public customTypes.customTypes_api.Vector3D getVector() + { + Log.i(TAG, "request getVector called, returning local"); + return m_vector; + } + + + @Override + public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) + { + Log.i(TAG, "request setExternVector called "+ extern_vector); + if (! m_extern_vector.equals(extern_vector)) + { + Message msg = new Message(); + msg.what = CounterMessageType.PROP_ExternVector.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(extern_vector)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) + { + Log.i(TAG, "value received from service for ExternVector "); + if (! m_extern_vector.equals(extern_vector)) + { + m_extern_vector = extern_vector; + fireExternVectorChanged(extern_vector); + } + + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D getExternVector() + { + Log.i(TAG, "request getExternVector called, returning local"); + return m_extern_vector; + } + + + @Override + public void setVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray) + { + Log.i(TAG, "request setVectorArray called "+ vectorArray); + if (! Arrays.equals(m_vectorArray, vectorArray)) + { + Message msg = new Message(); + msg.what = CounterMessageType.PROP_VectorArray.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(vectorArray)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray) + { + Log.i(TAG, "value received from service for VectorArray "); + if (! Arrays.equals(m_vectorArray, vectorArray)) + { + m_vectorArray = vectorArray; + fireVectorArrayChanged(vectorArray); + } + + } + + @Override + public customTypes.customTypes_api.Vector3D[] getVectorArray() + { + Log.i(TAG, "request getVectorArray called, returning local"); + return m_vectorArray; + } + + + @Override + public void setExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "request setExternVectorArray called "+ extern_vectorArray); + if (! Arrays.equals(m_extern_vectorArray, extern_vectorArray)) + { + Message msg = new Message(); + msg.what = CounterMessageType.PROP_ExternVectorArray.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(extern_vectorArray)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "value received from service for ExternVectorArray "); + if (! Arrays.equals(m_extern_vectorArray, extern_vectorArray)) + { + m_extern_vectorArray = extern_vectorArray; + fireExternVectorArrayChanged(extern_vectorArray); + } + + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVectorArray() + { + Log.i(TAG, "request getExternVectorArray called, returning local"); + return m_extern_vectorArray; + } + + + // methods + + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { + CompletableFuture resFuture = incrementAsync(vec); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture incrementAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { + + Log.i(TAG, "Call on service increment "+ " " + vec); + Message msg = new Message(); + msg.what = CounterMessageType.RPC_IncrementReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("vec", new externTypes.externTypes_android_messenger.MyVector3DParcelable(vec)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D result = bundle.getParcelable("result", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + Log.v(TAG, "resolve increment" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] incrementArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) { + CompletableFuture resFuture = incrementArrayAsync(vec); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture incrementArrayAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) { + + Log.i(TAG, "Call on service incrementArray "+ " " + vec); + Message msg = new Message(); + msg.what = CounterMessageType.RPC_IncrementArrayReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("vec", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(vec)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] result = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])bundle.getParcelableArray("result", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + Log.v(TAG, "resolve incrementArray" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public customTypes.customTypes_api.Vector3D decrement(customTypes.customTypes_api.Vector3D vec) { + CompletableFuture resFuture = decrementAsync(vec); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture decrementAsync(customTypes.customTypes_api.Vector3D vec) { + + Log.i(TAG, "Call on service decrement "+ " " + vec); + Message msg = new Message(); + msg.what = CounterMessageType.RPC_DecrementReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("vec", new customTypes.customTypes_android_messenger.Vector3DParcelable(vec)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + customTypes.customTypes_api.Vector3D result = bundle.getParcelable("result", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + Log.v(TAG, "resolve decrement" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec) { + CompletableFuture resFuture = decrementArrayAsync(vec); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture decrementArrayAsync(customTypes.customTypes_api.Vector3D[] vec) { + + Log.i(TAG, "Call on service decrementArray "+ " " + vec); + Message msg = new Message(); + msg.what = CounterMessageType.RPC_DecrementArrayReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("vec", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(vec)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + customTypes.customTypes_api.Vector3D[] result = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])bundle.getParcelableArray("result", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + Log.v(TAG, "resolve decrementArray" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "onValueChanged received from service"); + fireValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + } +} diff --git a/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java b/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java new file mode 100644 index 0000000..6692160 --- /dev/null +++ b/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java @@ -0,0 +1,520 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package counter.counter_android_client; + +import counter.counter_android_client.CounterClient; + +//import message type and parcelabe types +import counter.counter_api.CounterTestHelper; + +import counter.counter_api.ICounterEventListener; +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_android_messenger.CounterMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ICounterClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class CounterClientTest +{ + + @Mock + private Context mMockContext; + + private CounterClient testedClient; + private ICounterEventListener listenerMock = mock(ICounterEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ICounterClientMessageGetter serviceMessagesStorage = mock(ICounterClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(CounterMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ICounterClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new CounterClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("counter.counter_android_service", "counter.counter_android_service.CounterServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(CounterMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, CounterMessageType.INIT.getValue()); + Bundle data = new Bundle(); + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(testvector)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(testextern_vector)); + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(testvectorArray)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(testextern_vectorArray)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onVectorChanged(any(customTypes.customTypes_api.Vector3D.class)); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onExternVectorChanged(any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D.class)); + inOrderEventListener.verify(listenerMock,times(1)).onVectorArrayChanged(any(customTypes.customTypes_api.Vector3D[].class)); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onExternVectorArrayChanged(any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[].class)); + } + @Test + public void onReceivevectorPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.SET_Vector.getValue()); + Bundle data = new Bundle(); + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(testvector)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onVectorChanged(any(customTypes.customTypes_api.Vector3D.class)); + } + + @Test + public void setPropertyRequestvector() + { + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + testedClient.setVector(testvector); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.PROP_Vector.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D receivedvector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + assertEquals(receivedvector, testvector); + } + + @Test + public void onReceiveextern_vectorPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.SET_ExternVector.getValue()); + Bundle data = new Bundle(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(testextern_vector)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onExternVectorChanged(any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D.class)); + } + + /* + @Test + public void setPropertyRequestextern_vector() + { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + testedClient.setExternVector(testextern_vector); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.PROP_ExternVector.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D receivedextern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + assertEquals(receivedextern_vector, testextern_vector); + } + + */ + @Test + public void onReceivevectorArrayPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.SET_VectorArray.getValue()); + Bundle data = new Bundle(); + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(testvectorArray)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onVectorArrayChanged(any(customTypes.customTypes_api.Vector3D[].class)); + } + + @Test + public void setPropertyRequestvectorArray() + { + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + testedClient.setVectorArray(testvectorArray); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.PROP_VectorArray.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D[] receivedvectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + assertEquals(receivedvectorArray, testvectorArray); + } + + @Test + public void onReceiveextern_vectorArrayPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.SET_ExternVectorArray.getValue()); + Bundle data = new Bundle(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(testextern_vectorArray)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onExternVectorArrayChanged(any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[].class)); + } + + /* + @Test + public void setPropertyRequestextern_vectorArray() + { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + testedClient.setExternVectorArray(testextern_vectorArray); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.PROP_ExternVectorArray.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] receivedextern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + assertEquals(receivedextern_vectorArray, testextern_vectorArray); + } + + */ + @Test + public void whenNotifiedvalueChanged() throws RemoteException + { + + Message msg = Message.obtain(null, CounterMessageType.SIG_ValueChanged.getValue()); + Bundle data = new Bundle(); + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(testvector)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(testextern_vector)); + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(testvectorArray)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(testextern_vectorArray)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onValueChanged( any(customTypes.customTypes_api.Vector3D.class), any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D.class), any(customTypes.customTypes_api.Vector3D[].class), any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[].class)); + +} + + + public void onincrementRequest() throws RemoteException { + + // Execute method + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testvec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D expectedResult = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.incrementAsync(testvec); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(CounterMessageType.RPC_IncrementReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D receivedvec = data.getParcelable("vec", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + assertEquals(receivedvec, testvec); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, CounterMessageType.RPC_IncrementResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new externTypes.externTypes_android_messenger.MyVector3DParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onincrementArrayRequest() throws RemoteException { + + // Execute method + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testvec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testvec[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] expectedResult = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + expectedResult[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.incrementArrayAsync(testvec); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(CounterMessageType.RPC_IncrementArrayReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] receivedvec = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("vec", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + assertEquals(receivedvec, testvec); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, CounterMessageType.RPC_IncrementArrayResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void ondecrementRequest() throws RemoteException { + + // Execute method + customTypes.customTypes_api.Vector3D testvec = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + customTypes.customTypes_api.Vector3D expectedResult = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.decrementAsync(testvec); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(CounterMessageType.RPC_DecrementReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D receivedvec = data.getParcelable("vec", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + assertEquals(receivedvec, testvec); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, CounterMessageType.RPC_DecrementResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new customTypes.customTypes_android_messenger.Vector3DParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void ondecrementArrayRequest() throws RemoteException { + + // Execute method + customTypes.customTypes_api.Vector3D[] testvec = new customTypes.customTypes_api.Vector3D[1]; + testvec[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + customTypes.customTypes_api.Vector3D[] expectedResult = new customTypes.customTypes_api.Vector3D[1]; + expectedResult[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.decrementArrayAsync(testvec); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(CounterMessageType.RPC_DecrementArrayReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D[] receivedvec = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vec", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + assertEquals(receivedvec, testvec); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, CounterMessageType.RPC_DecrementArrayResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/counter/counter_android_messenger/additions.gradle b/goldenmaster/counter/counter_android_messenger/additions.gradle new file mode 100644 index 0000000..84852fe --- /dev/null +++ b/goldenmaster/counter/counter_android_messenger/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'counter.counter_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + api project(':customTypes_android_messenger') + api project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_android_messenger/build.gradle b/goldenmaster/counter/counter_android_messenger/build.gradle new file mode 100644 index 0000000..9f5bb38 --- /dev/null +++ b/goldenmaster/counter/counter_android_messenger/build.gradle @@ -0,0 +1,32 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "counter" +version = "1.0.0" + +android { + namespace 'counter.counter_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' + api 'customTypes:customTypes_android_messenger:1.0.0' + api 'externTypes:externTypes_android_messenger:1.0.0' +} diff --git a/goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterMessageType.java b/goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterMessageType.java new file mode 100644 index 0000000..9956e26 --- /dev/null +++ b/goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterMessageType.java @@ -0,0 +1,47 @@ +package counter.counter_android_messenger; + +public enum CounterMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_Vector(3), + SET_Vector(4), + PROP_ExternVector(5), + SET_ExternVector(6), + PROP_VectorArray(7), + SET_VectorArray(8), + PROP_ExternVectorArray(9), + SET_ExternVectorArray(10), + SIG_ValueChanged(11), + RPC_IncrementReq(12), + RPC_IncrementResp(13), + RPC_IncrementArrayReq(14), + RPC_IncrementArrayResp(15), + RPC_DecrementReq(16), + RPC_DecrementResp(17), + RPC_DecrementArrayReq(18), + RPC_DecrementArrayResp(19), + CounterMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + CounterMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static CounterMessageType fromInteger(int value) + { + for (CounterMessageType event : CounterMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return CounterMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterParcelable.java b/goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterParcelable.java new file mode 100644 index 0000000..6a2d7f7 --- /dev/null +++ b/goldenmaster/counter/counter_android_messenger/src/main/java/counter/counter_android_messenger/CounterParcelable.java @@ -0,0 +1,74 @@ +package counter.counter_android_messenger; + +import counter.counter_api.ICounter; +import android.os.Parcel; +import android.os.Parcelable; + + public class CounterParcelable implements Parcelable { + + public ICounter data; + + public CounterParcelable(ICounter data) { + this.data = data; + } + + public ICounter getCounter() + { + return data; + } + + protected CounterParcelable(Parcel in) { + customTypes.customTypes_android_messenger.Vector3DParcelable l_parcelablevector = in.readParcelable(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader(), customTypes.customTypes_android_messenger.Vector3DParcelable.class); + data.setVector(l_parcelablevector != null ? l_parcelablevector.data : null); + externTypes.externTypes_android_messenger.MyVector3DParcelable l_parcelableexternVector = in.readParcelable(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader(), externTypes.externTypes_android_messenger.MyVector3DParcelable.class); + data.setExternVector(l_parcelableexternVector != null ? l_parcelableexternVector.data : null); + customTypes.customTypes_android_messenger.Vector3DParcelable[] l_parcelablevectorArray = in.createTypedArray(customTypes.customTypes_android_messenger.Vector3DParcelable.CREATOR); + data.setVectorArray(customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray(l_parcelablevectorArray)); + externTypes.externTypes_android_messenger.MyVector3DParcelable[] l_parcelableexternVectorArray = in.createTypedArray(externTypes.externTypes_android_messenger.MyVector3DParcelable.CREATOR); + data.setExternVectorArray(externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray(l_parcelableexternVectorArray)); + } + + public static final Creator CREATOR = new Creator() { + @Override + public CounterParcelable createFromParcel(Parcel in) { + return new CounterParcelable(in); + } + + @Override + public CounterParcelable[] newArray(int size) { + return new CounterParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new customTypes.customTypes_android_messenger.Vector3DParcelable(data.getVector()), flags); + dest.writeParcelable(new externTypes.externTypes_android_messenger.MyVector3DParcelable(data.getExternVector()), flags); + dest.writeTypedArray(customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(data.getVectorArray()), flags); + dest.writeTypedArray(externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(data.getExternVectorArray()), flags); + + + } + public static CounterParcelable[] wrapArray(ICounter[] elements) { + if (elements == null) return null; + CounterParcelable[] out = new CounterParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new CounterParcelable(elements[i]); + } + return out; + } + + public static ICounter[] unwrapArray(CounterParcelable[] parcelables) { + if (parcelables == null) return null; + ICounter[] out = new ICounter[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getCounter(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/counter/counter_android_service/additions.gradle b/goldenmaster/counter/counter_android_service/additions.gradle new file mode 100644 index 0000000..ecf7629 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/additions.gradle @@ -0,0 +1,22 @@ +android { + namespace 'counter.counter_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + api project(':counter_impl') + api project(':counter_android_messenger') + api project(':customTypes_android_messenger') + api project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_android_service/build.gradle b/goldenmaster/counter/counter_android_service/build.gradle new file mode 100644 index 0000000..70b7e38 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/build.gradle @@ -0,0 +1,38 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "counter" +version = "1.0.0" + +android { + namespace 'counter.counter_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + implementation project(':counter_impl') + implementation project(':counter_android_messenger') + api 'customTypes:customTypes_android_messenger:1.0.0' + api 'externTypes:externTypes_android_messenger:1.0.0' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_android_service/src/main/AndroidManifest.xml b/goldenmaster/counter/counter_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..551f2cb --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java new file mode 100644 index 0000000..a620a09 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java @@ -0,0 +1,490 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package counter.counter_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import counter.counter_api.ICounterEventListener; +import counter.counter_android_service.ICounterServiceFactory; +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_android_messenger.CounterMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class CounterServiceAdapter extends Service +{ + private static final String TAG = "CounterServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private static Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ICounter mBackendService; + private static ICounterServiceFactory mServiceFactory; + private static final Object mutex = new Object(); + + //private final List mMessagesQueue = new ArrayList<>(); + + public CounterServiceAdapter() + { + } + + public static ICounter setService(ICounterServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + synchronized (mutex) + { + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(Counter) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(CounterService) called. context = " + this); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: CounterService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + Log.i(TAG, "LIFECYCLE: onDestroy(CounterService) - proc = " + ", mMessenger = " + mMessenger); + + if (mHandler != null) + { + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ICounterEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (CounterMessageType.fromInteger(msg.what) != CounterMessageType.REGISTER_CLIENT + && CounterMessageType.fromInteger(msg.what) != CounterMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: CounterMessageType" + CounterMessageType.fromInteger(msg.what) ); + return; + } + } + switch (CounterMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_Vector: + { + Bundle data = msg.getData(); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D vector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + mBackendService.setVector(vector); + break; + } + case PROP_ExternVector: + { + Bundle data = msg.getData(); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + mBackendService.setExternVector(extern_vector); + break; + } + case PROP_VectorArray: + { + Bundle data = msg.getData(); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D[] vectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + mBackendService.setVectorArray(vectorArray); + break; + } + case PROP_ExternVectorArray: + { + Bundle data = msg.getData(); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + mBackendService.setExternVectorArray(extern_vectorArray); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_IncrementReq: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec = data.getParcelable("vec", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D result = mBackendService.increment(vec); + + Message respMsg = new Message(); + respMsg.what = CounterMessageType.RPC_IncrementResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new externTypes.externTypes_android_messenger.MyVector3DParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_IncrementArrayReq: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("vec", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] result = mBackendService.incrementArray(vec); + + Message respMsg = new Message(); + respMsg.what = CounterMessageType.RPC_IncrementArrayResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_DecrementReq: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + customTypes.customTypes_api.Vector3D vec = data.getParcelable("vec", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + + customTypes.customTypes_api.Vector3D result = mBackendService.decrement(vec); + + Message respMsg = new Message(); + respMsg.what = CounterMessageType.RPC_DecrementResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new customTypes.customTypes_android_messenger.Vector3DParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_DecrementArrayReq: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + customTypes.customTypes_api.Vector3D[] vec = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vec", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + + customTypes.customTypes_api.Vector3D[] result = mBackendService.decrementArray(vec); + + Message respMsg = new Message(); + respMsg.what = CounterMessageType.RPC_DecrementArrayResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = CounterMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + customTypes.customTypes_api.Vector3D vector = mBackendService.getVector(); + + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(vector)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = mBackendService.getExternVector(); + + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(extern_vector)); + customTypes.customTypes_api.Vector3D[] vectorArray = mBackendService.getVectorArray(); + + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(vectorArray)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray = mBackendService.getExternVectorArray(); + + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(extern_vectorArray)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onVectorChanged(customTypes.customTypes_api.Vector3D vector){ + Log.i(TAG, "New value for Vector from backend" + vector); + + Message msg = new Message(); + msg.what = CounterMessageType.SET_Vector.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(vector)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector){ + Log.i(TAG, "New value for ExternVector from backend" + extern_vector); + + Message msg = new Message(); + msg.what = CounterMessageType.SET_ExternVector.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(extern_vector)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] vectorArray){ + Log.i(TAG, "New value for VectorArray from backend" + vectorArray); + + Message msg = new Message(); + msg.what = CounterMessageType.SET_VectorArray.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(vectorArray)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray){ + Log.i(TAG, "New value for ExternVectorArray from backend" + extern_vectorArray); + + Message msg = new Message(); + msg.what = CounterMessageType.SET_ExternVectorArray.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(extern_vectorArray)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray){ + Log.i(TAG, "New singal for ValueChanged = "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray); + Message msg = new Message(); + msg.what = CounterMessageType.SIG_ValueChanged.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(vector)); + + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(extern_vector)); + + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(vectorArray)); + + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(extern_vectorArray)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java new file mode 100644 index 0000000..322d527 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package counter.counter_android_service; + +import counter.counter_android_service.ICounterServiceFactory; +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_impl.CounterService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton CounterServiceFactory thread for the system. This is a thread for + * CounterServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class CounterServiceFactory extends HandlerThread implements ICounterServiceFactory +{ + private CounterService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static CounterServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: CounterServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractCounter getServiceInstance() + { + if (m_Service == null) + { + m_Service = new CounterService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new CounterService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final CounterServiceFactory INSTANCE = createInstance(); + } + + private CounterServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static CounterServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + CounterServiceFactory t = new CounterServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java new file mode 100644 index 0000000..93956f5 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java @@ -0,0 +1,42 @@ +package counter.counter_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import counter.counter_api.ICounterEventListener; +import counter.counter_api.ICounter; +import counter.counter_android_service.CounterServiceAdapter; +import counter.counter_android_service.CounterServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package counter.counter_impl; . +public class CounterServiceStarter { + + static Intent androidService = null; + private static final String TAG = "CounterStarter"; + + + + public static ICounter start(Context context) { + stop(context); + androidService = new Intent(context, CounterServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + CounterServiceFactory factory = CounterServiceFactory.get(); + Log.w(TAG, "starter: factory set for CounterServiceFactory"); + return CounterServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/ICounterServiceFactory.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/ICounterServiceFactory.java new file mode 100644 index 0000000..99e2bd4 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/ICounterServiceFactory.java @@ -0,0 +1,7 @@ +package counter.counter_android_service; +import counter.counter_api.ICounter; + + +public interface ICounterServiceFactory { + public ICounter getServiceInstance(); +} diff --git a/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java b/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java new file mode 100644 index 0000000..6c1b2bd --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java @@ -0,0 +1,540 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package counter.counter_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import counter.counter_android_service.CounterServiceAdapter; + +//import message type and parcelabe types +import counter.counter_api.CounterTestHelper; + + +import counter.counter_api.ICounterEventListener; +import counter.counter_android_service.ICounterServiceFactory; +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_android_messenger.CounterMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ICounterMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class CounterServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private CounterServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ICounterEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ICounter backendServiceMock = mock(ICounter.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ICounterServiceFactory serviceFactory = mock(ICounterServiceFactory.class); + private ICounterMessageGetter clientMessagesStorage = mock(ICounterMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, CounterMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ICounterMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + customTypes.customTypes_api.Vector3D initvector = new customTypes.customTypes_api.Vector3D(); + //TODO fill fields + when(backendServiceMock.getVector()).thenReturn(initvector); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D initextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + //TODO fill fields + when(backendServiceMock.getExternVector()).thenReturn(initextern_vector); + customTypes.customTypes_api.Vector3D init_elementvectorArray = new customTypes.customTypes_api.Vector3D(); + // todo fill if is struct + customTypes.customTypes_api.Vector3D[] initvectorArray = new customTypes.customTypes_api.Vector3D[]{ init_elementvectorArray } ; + when(backendServiceMock.getVectorArray()).thenReturn(initvectorArray); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D init_elementextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + // todo fill if is struct + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] initextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[]{ init_elementextern_vectorArray } ; + when(backendServiceMock.getExternVectorArray()).thenReturn(initextern_vectorArray); + + + Message registerMsg = Message.obtain(null, CounterMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getVector(); + inOrderBackendService.verify(backendServiceMock, times(1)).getExternVector(); + inOrderBackendService.verify(backendServiceMock, times(1)).getVectorArray(); + inOrderBackendService.verify(backendServiceMock, times(1)).getExternVectorArray(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + customTypes.customTypes_api.Vector3D receivedvector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D receivedextern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + + customTypes.customTypes_api.Vector3D[] receivedvectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] receivedextern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + // assertEquals(receivedvector, initvector); + // assertEquals(receivedextern_vector, initextern_vector); + // assertEquals(receivedvectorArray, initvectorArray); + // assertEquals(receivedextern_vectorArray, initextern_vectorArray); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, CounterServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(CounterServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ICounterEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void onReceivevectorPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.PROP_Vector.getValue()); + Bundle data = new Bundle(); + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelable("vector", new customTypes.customTypes_android_messenger.Vector3DParcelable(testvector)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setVector( any(customTypes.customTypes_api.Vector3D.class)); + + } + + @Test + public void whenNotifiedvector() + { + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + testedAdapterAsEventListener.onVectorChanged(testvector); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.SET_Vector.getValue(), response.what); + Bundle data = response.getData(); + + + customTypes.customTypes_api.Vector3D receivedvector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + + assertEquals(receivedvector, testvector); + } + @Test + public void onReceiveextern_vectorPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.PROP_ExternVector.getValue()); + Bundle data = new Bundle(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelable("extern_vector", new externTypes.externTypes_android_messenger.MyVector3DParcelable(testextern_vector)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setExternVector( any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D.class)); + + } + + @Test + public void whenNotifiedextern_vector() + { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + testedAdapterAsEventListener.onExternVectorChanged(testextern_vector); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.SET_ExternVector.getValue(), response.what); + Bundle data = response.getData(); + + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D receivedextern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + + assertEquals(receivedextern_vector, testextern_vector); + } + @Test + public void onReceivevectorArrayPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.PROP_VectorArray.getValue()); + Bundle data = new Bundle(); + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(testvectorArray)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setVectorArray( any(customTypes.customTypes_api.Vector3D[].class)); + + } + + @Test + public void whenNotifiedvectorArray() + { + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + testedAdapterAsEventListener.onVectorArrayChanged(testvectorArray); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.SET_VectorArray.getValue(), response.what); + Bundle data = response.getData(); + + + customTypes.customTypes_api.Vector3D[] receivedvectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + + assertEquals(receivedvectorArray, testvectorArray); + } + @Test + public void onReceiveextern_vectorArrayPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.PROP_ExternVectorArray.getValue()); + Bundle data = new Bundle(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(testextern_vectorArray)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setExternVectorArray( any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[].class)); + + } + + @Test + public void whenNotifiedextern_vectorArray() + { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + testedAdapterAsEventListener.onExternVectorArrayChanged(testextern_vectorArray); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.SET_ExternVectorArray.getValue(), response.what); + Bundle data = response.getData(); + + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] receivedextern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + + assertEquals(receivedextern_vectorArray, testextern_vectorArray); + } + @Test + public void whenNotifiedvalueChanged() + { + customTypes.customTypes_api.Vector3D testvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testextern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + customTypes.customTypes_api.Vector3D[] testvectorArray = new customTypes.customTypes_api.Vector3D[1]; + testvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testextern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testextern_vectorArray[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + testedAdapterAsEventListener.onValueChanged(testvector, testextern_vector, testvectorArray, testextern_vectorArray); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.SIG_ValueChanged.getValue(), response.what); + Bundle data = response.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + + customTypes.customTypes_api.Vector3D receivedvector = data.getParcelable("vector", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + assertEquals(receivedvector, testvector); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D receivedextern_vector = data.getParcelable("extern_vector", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + assertEquals(receivedextern_vector, testextern_vector); + + customTypes.customTypes_api.Vector3D[] receivedvectorArray = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])data.getParcelableArray("vectorArray", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + assertEquals(receivedvectorArray, testvectorArray); + + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] receivedextern_vectorArray = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])data.getParcelableArray("extern_vectorArray", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + assertEquals(receivedextern_vectorArray, testextern_vectorArray); +} + + + public void onincrementRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.RPC_IncrementReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D testvec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelable("vec", new externTypes.externTypes_android_messenger.MyVector3DParcelable(testvec)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D returnedValue = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + + when(backendServiceMock.increment( any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).increment( any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.RPC_IncrementResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D receivedByClient = resp_data.getParcelable("result", externTypes.externTypes_android_messenger.MyVector3DParcelable.class).getMyVector3D(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onincrementArrayRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.RPC_IncrementArrayReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] testvec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testvec[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + data.putParcelableArray("vec", externTypes.externTypes_android_messenger.MyVector3DParcelable.wrapArray(testvec)); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] returnedValue = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + returnedValue[0] = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + + + when(backendServiceMock.incrementArray( any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).incrementArray( any(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.RPC_IncrementArrayResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] receivedByClient = externTypes.externTypes_android_messenger.MyVector3DParcelable.unwrapArray((externTypes.externTypes_android_messenger.MyVector3DParcelable[])resp_data.getParcelableArray("result", externTypes.externTypes_android_messenger.MyVector3DParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void ondecrementRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.RPC_DecrementReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + customTypes.customTypes_api.Vector3D testvec = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelable("vec", new customTypes.customTypes_android_messenger.Vector3DParcelable(testvec)); + customTypes.customTypes_api.Vector3D returnedValue = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + + when(backendServiceMock.decrement( any(customTypes.customTypes_api.Vector3D.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).decrement( any(customTypes.customTypes_api.Vector3D.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.RPC_DecrementResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + customTypes.customTypes_api.Vector3D receivedByClient = resp_data.getParcelable("result", customTypes.customTypes_android_messenger.Vector3DParcelable.class).getVector3D(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void ondecrementArrayRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, CounterMessageType.RPC_DecrementArrayReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + customTypes.customTypes_api.Vector3D[] testvec = new customTypes.customTypes_api.Vector3D[1]; + testvec[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + data.putParcelableArray("vec", customTypes.customTypes_android_messenger.Vector3DParcelable.wrapArray(testvec)); + customTypes.customTypes_api.Vector3D[] returnedValue = new customTypes.customTypes_api.Vector3D[1]; + returnedValue[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + + + when(backendServiceMock.decrementArray( any(customTypes.customTypes_api.Vector3D[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).decrementArray( any(customTypes.customTypes_api.Vector3D[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(CounterMessageType.RPC_DecrementArrayResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + customTypes.customTypes_api.Vector3D[] receivedByClient = customTypes.customTypes_android_messenger.Vector3DParcelable.unwrapArray((customTypes.customTypes_android_messenger.Vector3DParcelable[])resp_data.getParcelableArray("result", customTypes.customTypes_android_messenger.Vector3DParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/counter/counter_api/additions.gradle b/goldenmaster/counter/counter_api/additions.gradle new file mode 100644 index 0000000..f5a9fb4 --- /dev/null +++ b/goldenmaster/counter/counter_api/additions.gradle @@ -0,0 +1,9 @@ +android { + namespace 'counter.counter_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + api project(':customTypes_api') + api project(':externTypes_api') +} diff --git a/goldenmaster/counter/counter_api/build.gradle b/goldenmaster/counter/counter_api/build.gradle new file mode 100644 index 0000000..edc9f1e --- /dev/null +++ b/goldenmaster/counter/counter_api/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java-library' +} + +group = "counter" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + api 'customTypes:customTypes_api:1.0.0' + api 'externTypes:externTypes_api:1.0.0' +} \ No newline at end of file diff --git a/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/AbstractCounter.java b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/AbstractCounter.java new file mode 100644 index 0000000..e323502 --- /dev/null +++ b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/AbstractCounter.java @@ -0,0 +1,59 @@ +package counter.counter_api; + +import counter.counter_api.ICounterEventListener; +import counter.counter_api.ICounter; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractCounter implements ICounter { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ICounterEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ICounterEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireVectorChanged(customTypes.customTypes_api.Vector3D newValue) { + for (ICounterEventListener listener : listeners) { + listener.onVectorChanged(newValue); + } + } + + @Override + public void fireExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) { + for (ICounterEventListener listener : listeners) { + listener.onExternVectorChanged(newValue); + } + } + + @Override + public void fireVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) { + for (ICounterEventListener listener : listeners) { + listener.onVectorArrayChanged(newValue); + } + } + + @Override + public void fireExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) { + for (ICounterEventListener listener : listeners) { + listener.onExternVectorArrayChanged(newValue); + } + } + + @Override + public void fireValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) { + for (ICounterEventListener listener : listeners) { + listener.onValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ICounterEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/CounterTestHelper.java b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/CounterTestHelper.java new file mode 100644 index 0000000..afd76f2 --- /dev/null +++ b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/CounterTestHelper.java @@ -0,0 +1,22 @@ +package counter.counter_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class CounterTestHelper +{ + + static public ICounter makeTestCounter(ICounter testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + customTypes.customTypes_api.Vector3D localvector = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + testObjToFill.setVector(localvector); + customTypes.customTypes_api.Vector3D[] localvectorArray = new customTypes.customTypes_api.Vector3D[1]; + localvectorArray[0] = customTypes.customTypes_api.CustomTypesTestHelper.makeTestVector3D(); + testObjToFill.setVectorArray(localvectorArray); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] localexternVectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[1]; + testObjToFill.setExternVectorArray(localexternVectorArray); + return testObjToFill; + } +} \ No newline at end of file diff --git a/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounter.java b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounter.java new file mode 100644 index 0000000..a351043 --- /dev/null +++ b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounter.java @@ -0,0 +1,41 @@ +package counter.counter_api; + +import counter.counter_api.ICounterEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface ICounter { + // properties + void setVector(customTypes.customTypes_api.Vector3D vector); + customTypes.customTypes_api.Vector3D getVector(); + void fireVectorChanged(customTypes.customTypes_api.Vector3D newValue); + + void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D getExternVector(); + void fireExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue); + + void setVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray); + customTypes.customTypes_api.Vector3D[] getVectorArray(); + void fireVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue); + + void setExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVectorArray(); + void fireExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue); + + // methods + org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec); + CompletableFuture incrementAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] incrementArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec); + CompletableFuture incrementArrayAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec); + customTypes.customTypes_api.Vector3D decrement(customTypes.customTypes_api.Vector3D vec); + CompletableFuture decrementAsync(customTypes.customTypes_api.Vector3D vec); + customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec); + CompletableFuture decrementArrayAsync(customTypes.customTypes_api.Vector3D[] vec); + public void fireValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ICounterEventListener listener); + void removeEventListener(ICounterEventListener listener); + } diff --git a/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounterEventListener.java b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounterEventListener.java new file mode 100644 index 0000000..ce5f5a7 --- /dev/null +++ b/goldenmaster/counter/counter_api/src/main/java/counter/counter_api/ICounterEventListener.java @@ -0,0 +1,10 @@ +package counter.counter_api; + + public interface ICounterEventListener { + void onVectorChanged(customTypes.customTypes_api.Vector3D newValue); + void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue); + void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue); + void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue); + void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/counter/counter_client_example/build.gradle b/goldenmaster/counter/counter_client_example/build.gradle new file mode 100644 index 0000000..b457dd9 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "counter" +version = "1.0.0" + +android { + namespace 'counter.counter_client_example' + compileSdk 35 + + defaultConfig { + applicationId "counter.counter_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':counter_api') + implementation project(':counter_android_messenger') + implementation project(':counter_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/AndroidManifest.xml b/goldenmaster/counter/counter_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..39c0289 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java b/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java new file mode 100644 index 0000000..28e2177 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java @@ -0,0 +1,322 @@ +package counter.counter_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import counter.counter_android_client.CounterClient; +import counter.counter_api.ICounterEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class CounterTestClientApp extends Activity implements ICounterEventListener +{ + + private static final String TAG = "CounterTestClientApp"; + + private CounterClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "counter.counterserviceexample.CounterTestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bVector = new Button(this); + bVector.setText("Set vector"); + bVector.setBackgroundColor(Color.GREEN); + + bVector.setOnClickListener(v -> { + customTypes.customTypes_api.Vector3D newVector = new customTypes.customTypes_api.Vector3D(mClient.getVector();); + //TODO increment + Log.i(TAG, "SET vector" + newVector); + mClient.setVector(newVector); + }); + propertyButtonsLine.addView(bVector); + Button bExternVector = new Button(this); + bExternVector.setText("Set extern_vector"); + bExternVector.setBackgroundColor(Color.GREEN); + + bExternVector.setOnClickListener(v -> { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D newExternVector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(mClient.getExternVector();); + //TODO increment + Log.i(TAG, "SET extern_vector" + newExternVector); + mClient.setExternVector(newExternVector); + }); + propertyButtonsLine.addView(bExternVector); + Button bVectorArray = new Button(this); + bVectorArray.setText("Set vectorArray"); + bVectorArray.setBackgroundColor(Color.GREEN); + + bVectorArray.setOnClickListener(v -> { + customTypes.customTypes_api.Vector3D[] newVectorArray = new customTypes.customTypes_api.Vector3D[](mClient.getVectorArray();); + //TODO increment + Log.i(TAG, "SET vectorArray" + newVectorArray); + mClient.setVectorArray(newVectorArray); + }); + propertyButtonsLine.addView(bVectorArray); + Button bExternVectorArray = new Button(this); + bExternVectorArray.setText("Set extern_vectorArray"); + bExternVectorArray.setBackgroundColor(Color.GREEN); + + bExternVectorArray.setOnClickListener(v -> { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newExternVectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[](mClient.getExternVectorArray();); + //TODO increment + Log.i(TAG, "SET extern_vectorArray" + newExternVectorArray); + mClient.setExternVectorArray(newExternVectorArray); + }); + propertyButtonsLine.addView(bExternVectorArray); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bIncrement = new Button(this); + bIncrement.setText("increment"); + + bIncrement.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD increment "); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + CompletableFuture method_res + = mClient.incrementAsync(vec).thenApply( + i -> { + outputTextVieMethodRes.setText("Got increment result "+ i); + return i; + }); + }); + bIncrement.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bIncrement); + Button bIncrementArray = new Button(this); + bIncrementArray.setText("incrementArray"); + + bIncrementArray.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD incrementArray "); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + CompletableFuture method_res + = mClient.incrementArrayAsync(vec).thenApply( + i -> { + outputTextVieMethodRes.setText("Got incrementArray result "+ i); + return i; + }); + }); + bIncrementArray.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bIncrementArray); + Button bDecrement = new Button(this); + bDecrement.setText("decrement"); + + bDecrement.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD decrement "); + customTypes.customTypes_api.Vector3D vec = new customTypes.customTypes_api.Vector3D(); + CompletableFuture method_res + = mClient.decrementAsync(vec).thenApply( + i -> { + outputTextVieMethodRes.setText("Got decrement result "+ i); + return i; + }); + }); + bDecrement.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bDecrement); + Button bDecrementArray = new Button(this); + bDecrementArray.setText("decrementArray"); + + bDecrementArray.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD decrementArray "); + customTypes.customTypes_api.Vector3D[] vec = new customTypes.customTypes_api.Vector3D(); + CompletableFuture method_res + = mClient.decrementArrayAsync(vec).thenApply( + i -> { + outputTextVieMethodRes.setText("Got decrementArray result "+ i); + return i; + }); + }); + bDecrementArray.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bDecrementArray); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new CounterClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) + { + outputTextViewProp.setText("Property from service: vector " + newValue); + Log.w(TAG, "Property from service: vector " + newValue); + } + @Override + public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) + { + outputTextViewProp.setText("Property from service: extern_vector " + newValue); + Log.w(TAG, "Property from service: extern_vector " + newValue); + } + @Override + public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) + { + outputTextViewProp.setText("Property from service: vectorArray " + newValue); + Log.w(TAG, "Property from service: vectorArray " + newValue); + } + @Override + public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) + { + outputTextViewProp.setText("Property from service: extern_vectorArray " + newValue); + Log.w(TAG, "Property from service: extern_vectorArray " + newValue); + } + @Override + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + String text = "Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counter_client_example/src/main/res/values-night/themes.xml b/goldenmaster/counter/counter_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/values/colors.xml b/goldenmaster/counter/counter_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/values/strings.xml b/goldenmaster/counter/counter_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..f4759f0 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + CounterTestClientApp + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/values/themes.xml b/goldenmaster/counter/counter_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/counter/counter_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/counter/counter_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/counter/counter_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counter_impl/additions.gradle b/goldenmaster/counter/counter_impl/additions.gradle new file mode 100644 index 0000000..5ef018d --- /dev/null +++ b/goldenmaster/counter/counter_impl/additions.gradle @@ -0,0 +1,19 @@ + +android { + namespace 'counter.counter_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_impl/build.gradle b/goldenmaster/counter/counter_impl/build.gradle new file mode 100644 index 0000000..9aa40d1 --- /dev/null +++ b/goldenmaster/counter/counter_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "counter" +version = "1.0.0" + +android { + namespace 'counter.counter_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':counter_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java new file mode 100644 index 0000000..80df5e2 --- /dev/null +++ b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java @@ -0,0 +1,202 @@ +package counter.counter_impl; + +import android.os.Messenger; +import android.util.Log; + +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_api.ICounterEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class CounterService extends AbstractCounter { + + private final static String TAG = "CounterService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private customTypes.customTypes_api.Vector3D m_vector = new customTypes.customTypes_api.Vector3D(); + private org.apache.commons.math3.geometry.euclidean.threed.Vector3D m_extern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + private customTypes.customTypes_api.Vector3D[] m_vectorArray = new customTypes.customTypes_api.Vector3D[]{}; + private org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] m_extern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[]{}; + + public CounterService() + { + fire_readyStatusChanged(true); + } + @Override + public void setVector(customTypes.customTypes_api.Vector3D vector) + { + Log.i(TAG, "request setVector called "); + if (! m_vector.equals(vector)) + { + m_vector = vector; + onVectorChanged(m_vector); + } + + } + + @Override + public customTypes.customTypes_api.Vector3D getVector() + { + Log.i(TAG, "request getVector called,"); + return m_vector; + } + + + @Override + public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) + { + Log.i(TAG, "request setExternVector called "); + if (! m_extern_vector.equals(extern_vector)) + { + m_extern_vector = extern_vector; + onExternVectorChanged(m_extern_vector); + } + + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D getExternVector() + { + Log.i(TAG, "request getExternVector called,"); + return m_extern_vector; + } + + + @Override + public void setVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray) + { + Log.i(TAG, "request setVectorArray called "); + if (! Arrays.equals(m_vectorArray, vectorArray)) + { + m_vectorArray = vectorArray; + onVectorArrayChanged(m_vectorArray); + } + + } + + @Override + public customTypes.customTypes_api.Vector3D[] getVectorArray() + { + Log.i(TAG, "request getVectorArray called,"); + return m_vectorArray; + } + + + @Override + public void setExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "request setExternVectorArray called "); + if (! Arrays.equals(m_extern_vectorArray, extern_vectorArray)) + { + m_extern_vectorArray = extern_vectorArray; + onExternVectorArrayChanged(m_extern_vectorArray); + } + + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVectorArray() + { + Log.i(TAG, "request getExternVectorArray called,"); + return m_extern_vectorArray; + } + + + // methods + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { + Log.w(TAG, "request method increment called, returnig default"); + return new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + } + + @Override + public CompletableFuture incrementAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { + return CompletableFuture.supplyAsync( + () -> {return increment(vec); }, + executor); + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] incrementArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) { + Log.w(TAG, "request method incrementArray called, returnig default"); + return new org.apache.commons.math3.geometry.euclidean.threed.Vector3D[]{}; + } + + @Override + public CompletableFuture incrementArrayAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) { + return CompletableFuture.supplyAsync( + () -> {return incrementArray(vec); }, + executor); + } + + @Override + public customTypes.customTypes_api.Vector3D decrement(customTypes.customTypes_api.Vector3D vec) { + Log.w(TAG, "request method decrement called, returnig default"); + return new customTypes.customTypes_api.Vector3D(); + } + + @Override + public CompletableFuture decrementAsync(customTypes.customTypes_api.Vector3D vec) { + return CompletableFuture.supplyAsync( + () -> {return decrement(vec); }, + executor); + } + + @Override + public customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec) { + Log.w(TAG, "request method decrementArray called, returnig default"); + return new customTypes.customTypes_api.Vector3D[]{}; + } + + @Override + public CompletableFuture decrementArrayAsync(customTypes.customTypes_api.Vector3D[] vec) { + return CompletableFuture.supplyAsync( + () -> {return decrementArray(vec); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) + { + Log.i(TAG, "onVectorChanged, will pass notification to all listeners"); + fireVectorChanged(newValue); + } + private void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) + { + Log.i(TAG, "onExternVectorChanged, will pass notification to all listeners"); + fireExternVectorChanged(newValue); + } + private void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) + { + Log.i(TAG, "onVectorArrayChanged, will pass notification to all listeners"); + fireVectorArrayChanged(newValue); + } + private void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) + { + Log.i(TAG, "onExternVectorArrayChanged, will pass notification to all listeners"); + fireExternVectorArrayChanged(newValue); + } + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "onValueChanged, will pass notification to all listeners"); + fireValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + } + +} \ No newline at end of file diff --git a/goldenmaster/counter/counterjniclient/CounterJniClient.java b/goldenmaster/counter/counterjniclient/CounterJniClient.java new file mode 100644 index 0000000..5cf9c63 --- /dev/null +++ b/goldenmaster/counter/counterjniclient/CounterJniClient.java @@ -0,0 +1,234 @@ +package counter.counterjniclient; + +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_api.ICounterEventListener; + +import counter.counter_android_client.CounterClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class CounterJniClient extends AbstractCounter implements ICounterEventListener +{ + + private static final String TAG = "CounterJniClient"; + + private CounterClient mMessengerClient = null; + + + private static String ModuleName = "counter.counterjniservice.CounterJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setVector(customTypes.customTypes_api.Vector3D vector) + { + Log.i(TAG, "got request from ue, setVector" + (vector)); + mMessengerClient.setVector(vector); + } + @Override + public customTypes.customTypes_api.Vector3D getVector() + { + Log.i(TAG, "got request from ue, getVector"); + return mMessengerClient.getVector(); + } + + @Override + public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) + { + Log.i(TAG, "got request from ue, setExternVector" + (extern_vector)); + mMessengerClient.setExternVector(extern_vector); + } + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D getExternVector() + { + Log.i(TAG, "got request from ue, getExternVector"); + return mMessengerClient.getExternVector(); + } + + @Override + public void setVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray) + { + Log.i(TAG, "got request from ue, setVectorArray" + (vectorArray)); + mMessengerClient.setVectorArray(vectorArray); + } + @Override + public customTypes.customTypes_api.Vector3D[] getVectorArray() + { + Log.i(TAG, "got request from ue, getVectorArray"); + return mMessengerClient.getVectorArray(); + } + + @Override + public void setExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "got request from ue, setExternVectorArray" + (extern_vectorArray)); + mMessengerClient.setExternVectorArray(extern_vectorArray); + } + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVectorArray() + { + Log.i(TAG, "got request from ue, getExternVectorArray"); + return mMessengerClient.getExternVectorArray(); + } + + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) + { + Log.v(TAG, "Blocking callincrement - should not be used "); + return mMessengerClient.increment(vec); + } + + public void incrementAsync(String callId, org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec){ + Log.v(TAG, "non blocking call increment "); + mMessengerClient.incrementAsync(vec).thenAccept(i -> { + nativeOnIncrementResult(i, callId);}); + } + + //Should not be called directly, use incrementAsync(String callId, org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) + public CompletableFuture incrementAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.incrementAsync(vec); + } + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] incrementArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) + { + Log.v(TAG, "Blocking callincrementArray - should not be used "); + return mMessengerClient.incrementArray(vec); + } + + public void incrementArrayAsync(String callId, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec){ + Log.v(TAG, "non blocking call incrementArray "); + mMessengerClient.incrementArrayAsync(vec).thenAccept(i -> { + nativeOnIncrementArrayResult(i, callId);}); + } + + //Should not be called directly, use incrementArrayAsync(String callId, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) + public CompletableFuture incrementArrayAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.incrementArrayAsync(vec); + } + public customTypes.customTypes_api.Vector3D decrement(customTypes.customTypes_api.Vector3D vec) + { + Log.v(TAG, "Blocking calldecrement - should not be used "); + return mMessengerClient.decrement(vec); + } + + public void decrementAsync(String callId, customTypes.customTypes_api.Vector3D vec){ + Log.v(TAG, "non blocking call decrement "); + mMessengerClient.decrementAsync(vec).thenAccept(i -> { + nativeOnDecrementResult(i, callId);}); + } + + //Should not be called directly, use decrementAsync(String callId, customTypes.customTypes_api.Vector3D vec) + public CompletableFuture decrementAsync(customTypes.customTypes_api.Vector3D vec) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.decrementAsync(vec); + } + public customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec) + { + Log.v(TAG, "Blocking calldecrementArray - should not be used "); + return mMessengerClient.decrementArray(vec); + } + + public void decrementArrayAsync(String callId, customTypes.customTypes_api.Vector3D[] vec){ + Log.v(TAG, "non blocking call decrementArray "); + mMessengerClient.decrementArrayAsync(vec).thenAccept(i -> { + nativeOnDecrementArrayResult(i, callId);}); + } + + //Should not be called directly, use decrementArrayAsync(String callId, customTypes.customTypes_api.Vector3D[] vec) + public CompletableFuture decrementArrayAsync(customTypes.customTypes_api.Vector3D[] vec) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.decrementArrayAsync(vec); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new CounterClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnVectorChanged(newValue); + } + @Override + public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnExternVectorChanged(newValue); + } + @Override + public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnVectorArrayChanged(newValue); + } + @Override + public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnExternVectorArrayChanged(newValue); + } + @Override + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray); + nativeOnValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + } + private native void nativeOnVectorChanged(customTypes.customTypes_api.Vector3D vector); + private native void nativeOnExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector); + private native void nativeOnVectorArrayChanged(customTypes.customTypes_api.Vector3D[] vectorArray); + private native void nativeOnExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray); + private native void nativeOnValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray); + private native void nativeOnIncrementResult(org.apache.commons.math3.geometry.euclidean.threed.Vector3D result, String callId); + private native void nativeOnIncrementArrayResult(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] result, String callId); + private native void nativeOnDecrementResult(customTypes.customTypes_api.Vector3D result, String callId); + private native void nativeOnDecrementArrayResult(customTypes.customTypes_api.Vector3D[] result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/counter/counterjniservice/CounterJniService.java b/goldenmaster/counter/counterjniservice/CounterJniService.java new file mode 100644 index 0000000..bf9802a --- /dev/null +++ b/goldenmaster/counter/counterjniservice/CounterJniService.java @@ -0,0 +1,201 @@ +package counter.counterjniservice; + +import android.os.Messenger; +import android.util.Log; + +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counter_api.ICounterEventListener; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class CounterJniService extends AbstractCounter { + + + private final static String TAG = "CounterJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public CounterJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setVector(customTypes.customTypes_api.Vector3D vector) + { + Log.i(TAG, "request setVector called, will call native "); + nativeSetVector(vector); + } + + @Override + public customTypes.customTypes_api.Vector3D getVector() + { + Log.i(TAG, "request getVector called, will call native "); + return nativeGetVector(); + } + + + @Override + public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) + { + Log.i(TAG, "request setExternVector called, will call native "); + nativeSetExternVector(extern_vector); + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D getExternVector() + { + Log.i(TAG, "request getExternVector called, will call native "); + return nativeGetExternVector(); + } + + + @Override + public void setVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray) + { + Log.i(TAG, "request setVectorArray called, will call native "); + nativeSetVectorArray(vectorArray); + } + + @Override + public customTypes.customTypes_api.Vector3D[] getVectorArray() + { + Log.i(TAG, "request getVectorArray called, will call native "); + return nativeGetVectorArray(); + } + + + @Override + public void setExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "request setExternVectorArray called, will call native "); + nativeSetExternVectorArray(extern_vectorArray); + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVectorArray() + { + Log.i(TAG, "request getExternVectorArray called, will call native "); + return nativeGetExternVectorArray(); + } + + + // methods + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { + Log.w(TAG, "request method increment called, will call native"); + return nativeIncrement(vec); + } + + @Override + public CompletableFuture incrementAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { + return CompletableFuture.supplyAsync( + () -> {return increment(vec); }, + executor); + } + + @Override + public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] incrementArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) { + Log.w(TAG, "request method incrementArray called, will call native"); + return nativeIncrementArray(vec); + } + + @Override + public CompletableFuture incrementArrayAsync(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec) { + return CompletableFuture.supplyAsync( + () -> {return incrementArray(vec); }, + executor); + } + + @Override + public customTypes.customTypes_api.Vector3D decrement(customTypes.customTypes_api.Vector3D vec) { + Log.w(TAG, "request method decrement called, will call native"); + return nativeDecrement(vec); + } + + @Override + public CompletableFuture decrementAsync(customTypes.customTypes_api.Vector3D vec) { + return CompletableFuture.supplyAsync( + () -> {return decrement(vec); }, + executor); + } + + @Override + public customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec) { + Log.w(TAG, "request method decrementArray called, will call native"); + return nativeDecrementArray(vec); + } + + @Override + public CompletableFuture decrementArrayAsync(customTypes.customTypes_api.Vector3D[] vec) { + return CompletableFuture.supplyAsync( + () -> {return decrementArray(vec); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetVector(customTypes.customTypes_api.Vector3D vector); + private native customTypes.customTypes_api.Vector3D nativeGetVector(); + + private native void nativeSetExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector); + private native org.apache.commons.math3.geometry.euclidean.threed.Vector3D nativeGetExternVector(); + + private native void nativeSetVectorArray(customTypes.customTypes_api.Vector3D[] vectorArray); + private native customTypes.customTypes_api.Vector3D[] nativeGetVectorArray(); + + private native void nativeSetExternVectorArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray); + private native org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] nativeGetExternVectorArray(); + + // methods + private native org.apache.commons.math3.geometry.euclidean.threed.Vector3D nativeIncrement(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec); + private native org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] nativeIncrementArray(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec); + private native customTypes.customTypes_api.Vector3D nativeDecrement(customTypes.customTypes_api.Vector3D vec); + private native customTypes.customTypes_api.Vector3D[] nativeDecrementArray(customTypes.customTypes_api.Vector3D[] vec); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) + { + Log.i(TAG, "onVectorChanged, will pass notification to all listeners"); + fireVectorChanged(newValue); + } + public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) + { + Log.i(TAG, "onExternVectorChanged, will pass notification to all listeners"); + fireExternVectorChanged(newValue); + } + public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) + { + Log.i(TAG, "onVectorArrayChanged, will pass notification to all listeners"); + fireVectorArrayChanged(newValue); + } + public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) + { + Log.i(TAG, "onExternVectorArrayChanged, will pass notification to all listeners"); + fireExternVectorArrayChanged(newValue); + } + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + Log.i(TAG, "onValueChanged, will pass notification to all listeners"); + fireValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + } + +} diff --git a/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java b/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java new file mode 100644 index 0000000..d02cc18 --- /dev/null +++ b/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package counter.counterjniservice; + +import counter.counter_android_service.ICounterServiceFactory; +import counter.counter_api.ICounter; +import counter.counter_api.AbstractCounter; +import counter.counterjniservice.CounterJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton CounterJniServiceFactory thread for the system. This is a thread for + * CounterJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class CounterJniServiceFactory extends HandlerThread implements ICounterServiceFactory +{ + private CounterJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static CounterJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: CounterJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractCounter getServiceInstance() + { + if (jniService == null) + { + jniService = new CounterJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new CounterJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final CounterJniServiceFactory INSTANCE = createInstance(); + } + + private CounterJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static CounterJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + CounterJniServiceFactory t = new CounterJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java b/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java new file mode 100644 index 0000000..1fb1ef2 --- /dev/null +++ b/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java @@ -0,0 +1,42 @@ +package counter.counterjniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import counter.counter_api.ICounterEventListener; +import counter.counter_api.ICounter; +import counter.counter_android_service.CounterServiceAdapter; +import counter.counterjniservice.CounterJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class CounterJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "CounterJniStarter"; + + + + public static ICounter start(Context context) { + stop(context); + androidService = new Intent(context, CounterServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + CounterJniServiceFactory factory = CounterJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for CounterJniServiceFactory"); + return CounterServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/build.gradle b/goldenmaster/counter/counterserviceexample/build.gradle new file mode 100644 index 0000000..4345ea3 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "counter" +version = "1.0.0" + + +android { + namespace 'counter.counterserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "counter.counterserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':counter_api') + implementation project(':counter_android_messenger') + implementation project(':counter_android_service') + implementation project(':counter_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/AndroidManifest.xml b/goldenmaster/counter/counterserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5540981 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + diff --git a/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java b/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java new file mode 100644 index 0000000..c39e34c --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java @@ -0,0 +1,256 @@ +package counter.counterserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import counter.counter_android_service.CounterServiceAdapter; +import counter.counter_android_service.CounterServiceFactory; +import counter.counter_android_service.CounterServiceStarter; + +//import message type and parcelabe types + +import counter.counter_api.ICounterEventListener; +import counter.counter_api.ICounter; +import counter.counter_impl.CounterService; +import java.util.concurrent.CompletableFuture; + + + +public class CounterTestServiceApp extends Activity implements ICounterEventListener +{ + + private static final String TAG = "CounterTestServiceApp"; + static Intent stub_service = null; + + + private ICounter mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bVector = new Button(this); + bVector.setText("Set vector"); + bVector.setBackgroundColor(Color.GREEN); + + bVector.setOnClickListener(v -> { + customTypes.customTypes_api.Vector3D newVector = mBackend.getVector(); + //TODO increment + Log.i(TAG, "SET vector" + newVector); + mBackend.setVector(newVector); + }); + propertyButtonsLine.addView(bVector); + Button bExternVector = new Button(this); + bExternVector.setText("Set extern_vector"); + bExternVector.setBackgroundColor(Color.GREEN); + + bExternVector.setOnClickListener(v -> { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D newExternVector = mBackend.getExternVector(); + //TODO increment + Log.i(TAG, "SET extern_vector" + newExternVector); + mBackend.setExternVector(newExternVector); + }); + propertyButtonsLine.addView(bExternVector); + Button bVectorArray = new Button(this); + bVectorArray.setText("Set vectorArray"); + bVectorArray.setBackgroundColor(Color.GREEN); + + bVectorArray.setOnClickListener(v -> { + customTypes.customTypes_api.Vector3D[] newVectorArray = mBackend.getVectorArray(); + //TODO increment + Log.i(TAG, "SET vectorArray" + newVectorArray); + mBackend.setVectorArray(newVectorArray); + }); + propertyButtonsLine.addView(bVectorArray); + Button bExternVectorArray = new Button(this); + bExternVectorArray.setText("Set extern_vectorArray"); + bExternVectorArray.setBackgroundColor(Color.GREEN); + + bExternVectorArray.setOnClickListener(v -> { + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newExternVectorArray = mBackend.getExternVectorArray(); + //TODO increment + Log.i(TAG, "SET extern_vectorArray" + newExternVectorArray); + mBackend.setExternVectorArray(newExternVectorArray); + }); + propertyButtonsLine.addView(bExternVectorArray); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bValueChanged = new Button(this); + bValueChanged.setText("valueChanged"); + + bValueChanged.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal valueChanged "); + customTypes.customTypes_api.Vector3D vector = new customTypes.customTypes_api.Vector3D(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + customTypes.customTypes_api.Vector3D[] vectorArray = new customTypes.customTypes_api.Vector3D(); + org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); + mBackend.fireValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); + }); + bValueChanged.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bValueChanged); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, CounterServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = CounterServiceAdapter.setService(CounterServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) + { + outputTextViewProp.setText("Property from service: vector " + newValue); + Log.w(TAG, "Property from service: vector " + newValue); + } + @Override + public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) + { + outputTextViewProp.setText("Property from service: extern_vector " + newValue); + Log.w(TAG, "Property from service: extern_vector " + newValue); + } + @Override + public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) + { + outputTextViewProp.setText("Property from service: vectorArray " + newValue); + Log.w(TAG, "Property from service: vectorArray " + newValue); + } + @Override + public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) + { + outputTextViewProp.setText("Property from service: extern_vectorArray " + newValue); + Log.w(TAG, "Property from service: extern_vectorArray " + newValue); + } + @Override + public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) + { + String text = "Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 zcmV-Y3$650Nk&FW3jhFDMM6+kP&il$0000G0000-002h-06|PpNWB9900E$G+qN-D z+81ABX7q?;bwx%xBg?kcwr$(C-Tex-ZCkHUw(Y9#+`E5-zuONG5fgw~E2WDng@Bc@ z24xy+R1n%~6xI#u9vJ8zREI)sb<&Il(016}Z~V1n^PU3-_H17A*Bf^o)&{_uBv}Py zulRfeE8g(g6HFhk_?o_;0@tz?1I+l+Y#Q*;RVC?(ud`_cU-~n|AX-b`JHrOIqn(-t&rOg-o`#C zh0LPxmbOAEb;zHTu!R3LDh1QO zZTf-|lJNUxi-PpcbRjw3n~n-pG;$+dIF6eqM5+L();B2O2tQ~|p{PlpNcvDbd1l%c zLtXn%lu(3!aNK!V#+HNn_D3lp z2%l+hK-nsj|Bi9;V*WIcQRTt5j90A<=am+cc`J zTYIN|PsYAhJ|=&h*4wI4ebv-C=Be#u>}%m;a{IGmJDU`0snWS&$9zdrT(z8#{OZ_Y zxwJx!ZClUi%YJjD6Xz@OP8{ieyJB=tn?>zaI-4JN;rr`JQbb%y5h2O-?_V@7pG_+y z(lqAsqYr!NyVb0C^|uclHaeecG)Sz;WV?rtoqOdAAN{j%?Uo%owya(F&qps@Id|Of zo@~Y-(YmfB+chv^%*3g4k3R0WqvuYUIA+8^SGJ{2Bl$X&X&v02>+0$4?di(34{pt* zG=f#yMs@Y|b&=HyH3k4yP&goF2LJ#tBLJNNDo6lG06r}ghC-pC4Q*=x3;|+W04zte zAl>l4kzUBQFYF(E`KJy?ZXd1tnfbH+Z~SMmA21KokJNs#eqcXWKUIC>{TuoKe^vhF z);H)o`t9j~`$h1D`#bxe@E`oE`cM9w(@)5Bp8BNukIwM>wZHfd0S;5bcXA*5KT3bj zc&_~`&{z7u{Et!Z_k78H75gXf4g8<_ul!H$eVspPeU3j&&Au=2R*Zp#M9$9s;fqwgzfiX=E_?BwVcfx3tG9Q-+<5fw z%Hs64z)@Q*%s3_Xd5>S4dg$s>@rN^ixeVj*tqu3ZV)biDcFf&l?lGwsa zWj3rvK}?43c{IruV2L`hUU0t^MemAn3U~x3$4mFDxj=Byowu^Q+#wKRPrWywLjIAp z9*n}eQ9-gZmnd9Y0WHtwi2sn6n~?i#n9VN1B*074_VbZZ=WrpkMYr{RsI ztM_8X1)J*DZejxkjOTRJ&a*lrvMKBQURNP#K)a5wIitfu(CFYV4FT?LUB$jVwJSZz zNBFTWg->Yk0j&h3e*a5>B=-xM7dE`IuOQna!u$OoxLlE;WdrNlN)1 z7**de7-hZ!(%_ZllHBLg`Ir#|t>2$*xVOZ-ADZKTN?{(NUeLU9GbuG-+Axf*AZ-P1 z0ZZ*fx+ck4{XtFsbcc%GRStht@q!m*ImssGwuK+P@%gEK!f5dHymg<9nSCXsB6 zQ*{<`%^bxB($Z@5286^-A(tR;r+p7B%^%$N5h%lb*Vlz-?DL9x;!j<5>~kmXP$E}m zQV|7uv4SwFs0jUervsxVUm>&9Y3DBIzc1XW|CUZrUdb<&{@D5yuLe%Xniw^x&{A2s z0q1+owDSfc3Gs?ht;3jw49c#mmrViUfX-yvc_B*wY|Lo7; zGh!t2R#BHx{1wFXReX*~`NS-LpSX z#TV*miO^~B9PF%O0huw!1Zv>^d0G3$^8dsC6VI!$oKDKiXdJt{mGkyA`+Gwd4D-^1qtNTUK)`N*=NTG-6}=5k6suNfdLt*dt8D| z%H#$k)z#ZRcf|zDWB|pn<3+7Nz>?WW9WdkO5(a^m+D4WRJ9{wc>Y}IN)2Kbgn;_O? zGqdr&9~|$Y0tP=N(k7^Eu;iO*w+f%W`20BNo)=Xa@M_)+o$4LXJyiw{F?a633SC{B zl~9FH%?^Rm*LVz`lkULs)%idDX^O)SxQol(3jDRyBVR!7d`;ar+D7do)jQ}m`g$TevUD5@?*P8)voa?kEe@_hl{_h8j&5eB-5FrYW&*FHVt$ z$kRF9Nstj%KRzpjdd_9wO=4zO8ritN*NPk_9avYrsF(!4))tm{Ga#OY z(r{0buexOzu7+rw8E08Gxd`LTOID{*AC1m*6Nw@osfB%0oBF5sf<~wH1kL;sd zo)k6^VyRFU`)dt*iX^9&QtWbo6yE8XXH?`ztvpiOLgI3R+=MOBQ9=rMVgi<*CU%+d1PQQ0a1U=&b0vkF207%xU0ssI2 literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/counter/counterserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/values/colors.xml b/goldenmaster/counter/counterserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/values/strings.xml b/goldenmaster/counter/counterserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..801f622 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + CounterTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/values/themes.xml b/goldenmaster/counter/counterserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/counter/counterserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/counter/counterserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/counter/gradle.properties b/goldenmaster/counter/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/counter/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/counter/gradle/libs.versions.toml b/goldenmaster/counter/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/counter/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/counter/settings.gradle b/goldenmaster/counter/settings.gradle new file mode 100644 index 0000000..25fd6c6 --- /dev/null +++ b/goldenmaster/counter/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "counter" +include ':counter_android_service' +include ':counter_android_client' +include ':counter_android_messenger' +include ':counter_impl' +include ':counter_api' +include ':counter_client_example' +include ':counterserviceexample' \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_android_client/additions.gradle b/goldenmaster/customTypes/customTypes_android_client/additions.gradle new file mode 100644 index 0000000..edb4a49 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_client/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'customTypes.customTypes_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + implementation project(':customTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_android_client/build.gradle b/goldenmaster/customTypes/customTypes_android_client/build.gradle new file mode 100644 index 0000000..fdefa6b --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "customTypes" +version = "1.0.0" + +android { + namespace 'customTypes.customTypes_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + implementation project(':customTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_android_messenger/additions.gradle b/goldenmaster/customTypes/customTypes_android_messenger/additions.gradle new file mode 100644 index 0000000..9730dd2 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'customTypes.customTypes_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_android_messenger/build.gradle b/goldenmaster/customTypes/customTypes_android_messenger/build.gradle new file mode 100644 index 0000000..a603a15 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "customTypes" +version = "1.0.0" + +android { + namespace 'customTypes.customTypes_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_android_messenger/src/main/java/customTypes/customTypes_android_messenger/Vector3DParcelable.java b/goldenmaster/customTypes/customTypes_android_messenger/src/main/java/customTypes/customTypes_android_messenger/Vector3DParcelable.java new file mode 100644 index 0000000..407bc6c --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_messenger/src/main/java/customTypes/customTypes_android_messenger/Vector3DParcelable.java @@ -0,0 +1,69 @@ +package customTypes.customTypes_android_messenger; + +import customTypes.customTypes_api.Vector3D; +import android.os.Parcel; +import android.os.Parcelable; + + public class Vector3DParcelable implements Parcelable { + + public Vector3D data; + + public Vector3DParcelable(Vector3D data) { + this.data = new Vector3D(data); + } + + public Vector3D getVector3D() + { + return new Vector3D(data); + } + + protected Vector3DParcelable(Parcel in) { + this.data = new Vector3D(); + data.x = in.readFloat(); + data.y = in.readFloat(); + data.z = in.readFloat(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Vector3DParcelable createFromParcel(Parcel in) { + return new Vector3DParcelable(in); + } + + @Override + public Vector3DParcelable[] newArray(int size) { + return new Vector3DParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(data.x); + dest.writeFloat(data.y); + dest.writeFloat(data.z); + + + } + public static Vector3DParcelable[] wrapArray(Vector3D[] structs) { + if (structs == null) return null; + Vector3DParcelable[] out = new Vector3DParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new Vector3DParcelable(structs[i]); + } + return out; + } + + public static Vector3D[] unwrapArray(Vector3DParcelable[] parcelables) { + if (parcelables == null) return null; + Vector3D[] out = new Vector3D[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getVector3D(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/customTypes/customTypes_android_service/additions.gradle b/goldenmaster/customTypes/customTypes_android_service/additions.gradle new file mode 100644 index 0000000..d617a37 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_service/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'customTypes.customTypes_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + api project(':customTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_android_service/build.gradle b/goldenmaster/customTypes/customTypes_android_service/build.gradle new file mode 100644 index 0000000..abda0a8 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_service/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "customTypes" +version = "1.0.0" + +android { + namespace 'customTypes.customTypes_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + implementation project(':customTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_android_service/src/main/AndroidManifest.xml b/goldenmaster/customTypes/customTypes_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f8c3eef --- /dev/null +++ b/goldenmaster/customTypes/customTypes_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_api/additions.gradle b/goldenmaster/customTypes/customTypes_api/additions.gradle new file mode 100644 index 0000000..87a1f76 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'customTypes.customTypes_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/customTypes/customTypes_api/build.gradle b/goldenmaster/customTypes/customTypes_api/build.gradle new file mode 100644 index 0000000..d6cb3d2 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "customTypes" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/CustomTypesTestHelper.java b/goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/CustomTypesTestHelper.java new file mode 100644 index 0000000..f29aada --- /dev/null +++ b/goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/CustomTypesTestHelper.java @@ -0,0 +1,18 @@ +package customTypes.customTypes_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class CustomTypesTestHelper +{ + + static public Vector3D makeTestVector3D() + { + Vector3D testStruct = new Vector3D(); + testStruct.x = 1.0f; + testStruct.y = 1.0f; + testStruct.z = 1.0f; + return testStruct; + } +} \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/Vector3D.java b/goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/Vector3D.java new file mode 100644 index 0000000..f29f7b4 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_api/src/main/java/customTypes/customTypes_api/Vector3D.java @@ -0,0 +1,55 @@ +package customTypes.customTypes_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class Vector3D { + + public Vector3D(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3D() + { + } + @JsonProperty("x") + public float x; + @JsonProperty("y") + public float y; + @JsonProperty("z") + public float z; + + public Vector3D(Vector3D other) + { + this.x = other.x; + this.y = other.y; + this.z = other.z; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Vector3D)) return false; + Vector3D other = (Vector3D) o; + + return + this.x == other.x + && this.y == other.y + && this.z == other.z; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Float.hashCode(x); + result = 31 * result + Float.hashCode(y); + result = 31 * result + Float.hashCode(z); + return result; + } + + +} diff --git a/goldenmaster/customTypes/customTypes_client_example/build.gradle b/goldenmaster/customTypes/customTypes_client_example/build.gradle new file mode 100644 index 0000000..ede7990 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "customTypes" +version = "1.0.0" + +android { + namespace 'customTypes.customTypes_client_example' + compileSdk 35 + + defaultConfig { + applicationId "customTypes.customTypes_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':customTypes_api') + implementation project(':customTypes_android_messenger') + implementation project(':customTypes_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/AndroidManifest.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a4c1255 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/java/customTypes/customTypes_client_example/CustomTypesTestClientApp.java b/goldenmaster/customTypes/customTypes_client_example/src/main/java/customTypes/customTypes_client_example/CustomTypesTestClientApp.java new file mode 100644 index 0000000..21de86a --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/java/customTypes/customTypes_client_example/CustomTypesTestClientApp.java @@ -0,0 +1,13 @@ +package customTypes.customTypes_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/values-night/themes.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/colors.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/strings.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..e8c810c --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + CustomTypesTestClientApp + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/themes.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/customTypes/customTypes_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypes_impl/additions.gradle b/goldenmaster/customTypes/customTypes_impl/additions.gradle new file mode 100644 index 0000000..7efbedb --- /dev/null +++ b/goldenmaster/customTypes/customTypes_impl/additions.gradle @@ -0,0 +1,19 @@ + +android { + namespace 'customTypes.customTypes_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypes_impl/build.gradle b/goldenmaster/customTypes/customTypes_impl/build.gradle new file mode 100644 index 0000000..75530da --- /dev/null +++ b/goldenmaster/customTypes/customTypes_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "customTypes" +version = "1.0.0" + +android { + namespace 'customTypes.customTypes_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':customTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/customTypes/customTypesserviceexample/build.gradle b/goldenmaster/customTypes/customTypesserviceexample/build.gradle new file mode 100644 index 0000000..157b7e8 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "customTypes" +version = "1.0.0" + + +android { + namespace 'customTypes.customTypesserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "customTypes.customTypesserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':customTypes_api') + implementation project(':customTypes_android_messenger') + implementation project(':customTypes_android_service') + implementation project(':customTypes_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/AndroidManifest.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..70df13b --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/java/customTypes/customTypesserviceexample/CustomTypesTestServiceApp.java b/goldenmaster/customTypes/customTypesserviceexample/src/main/java/customTypes/customTypesserviceexample/CustomTypesTestServiceApp.java new file mode 100644 index 0000000..97a019f --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/java/customTypes/customTypesserviceexample/CustomTypesTestServiceApp.java @@ -0,0 +1,14 @@ +package customTypes.customTypesserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/colors.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/strings.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..eab0c0b --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + CustomTypesTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/themes.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/customTypes/gradle.properties b/goldenmaster/customTypes/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/customTypes/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/customTypes/gradle/libs.versions.toml b/goldenmaster/customTypes/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/customTypes/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/customTypes/settings.gradle b/goldenmaster/customTypes/settings.gradle new file mode 100644 index 0000000..5d6cbe2 --- /dev/null +++ b/goldenmaster/customTypes/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "customTypes" +include ':customTypes_android_service' +include ':customTypes_android_client' +include ':customTypes_android_messenger' +include ':customTypes_impl' +include ':customTypes_api' +include ':customTypes_client_example' +include ':customTypesserviceexample' \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_android_client/additions.gradle b/goldenmaster/externTypes/externTypes_android_client/additions.gradle new file mode 100644 index 0000000..1613ac9 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_client/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'externTypes.externTypes_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + implementation project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_android_client/build.gradle b/goldenmaster/externTypes/externTypes_android_client/build.gradle new file mode 100644 index 0000000..d7be8c6 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "externTypes" +version = "1.0.0" + +android { + namespace 'externTypes.externTypes_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + implementation project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_android_messenger/additions.gradle b/goldenmaster/externTypes/externTypes_android_messenger/additions.gradle new file mode 100644 index 0000000..f784920 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'externTypes.externTypes_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_android_messenger/build.gradle b/goldenmaster/externTypes/externTypes_android_messenger/build.gradle new file mode 100644 index 0000000..5f7b2a2 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "externTypes" +version = "1.0.0" + +android { + namespace 'externTypes.externTypes_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_android_messenger/src/main/java/externTypes/externTypes_android_messenger/MyVector3DParcelable.java b/goldenmaster/externTypes/externTypes_android_messenger/src/main/java/externTypes/externTypes_android_messenger/MyVector3DParcelable.java new file mode 100644 index 0000000..d9edba7 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_messenger/src/main/java/externTypes/externTypes_android_messenger/MyVector3DParcelable.java @@ -0,0 +1,66 @@ +package externTypes.externTypes_android_messenger; +import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; +import android.os.Parcel; +import android.os.Parcelable; + + + public class MyVector3DParcelable implements Parcelable { + public Vector3D data; + + public MyVector3DParcelable(Vector3D data) { + // WARNING Copy if not simple type + this.data = data; + } + + public Vector3D getMyVector3D() + { + // WARNING Copy if not simple type. + return data; + } + + protected MyVector3DParcelable(Parcel in) { + //WARNING Fill the data field by field with in.createTypedArray, in. read[dataType] or in.readParcelable, depending on type. + } + + public static final Creator CREATOR = new Creator() { + @Override + public MyVector3DParcelable createFromParcel(Parcel in) { + return new MyVector3DParcelable(in); + } + + @Override + public MyVector3DParcelable[] newArray(int size) { + return new MyVector3DParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + // WARNING Fill dest field by field with dest.write[TypedArray/Type/Parcelabe](data.field, flags); + } + + // Helpers for arrays of this type + public static MyVector3DParcelable[] wrapArray(Vector3D[] elements) + { + if (elements == null) return null; + MyVector3DParcelable[] out = new MyVector3DParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new MyVector3DParcelable(elements[i]); + } + return out; + } + + public static Vector3D[] unwrapArray(MyVector3DParcelable[] parcelables) { + if (parcelables == null) return null; + Vector3D[] out = new Vector3D[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getMyVector3D(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/externTypes/externTypes_android_service/additions.gradle b/goldenmaster/externTypes/externTypes_android_service/additions.gradle new file mode 100644 index 0000000..dbdb8b0 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_service/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'externTypes.externTypes_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + api project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_android_service/build.gradle b/goldenmaster/externTypes/externTypes_android_service/build.gradle new file mode 100644 index 0000000..4ce9988 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_service/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "externTypes" +version = "1.0.0" + +android { + namespace 'externTypes.externTypes_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + implementation project(':externTypes_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_android_service/src/main/AndroidManifest.xml b/goldenmaster/externTypes/externTypes_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..752e1b8 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_api/additions.gradle b/goldenmaster/externTypes/externTypes_api/additions.gradle new file mode 100644 index 0000000..de0c010 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_api/additions.gradle @@ -0,0 +1,8 @@ +android { + namespace 'externTypes.externTypes_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + api 'org.apache.commons:commons-math3:3.6.1' +} diff --git a/goldenmaster/externTypes/externTypes_api/build.gradle b/goldenmaster/externTypes/externTypes_api/build.gradle new file mode 100644 index 0000000..ed1d33d --- /dev/null +++ b/goldenmaster/externTypes/externTypes_api/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java-library' +} + +group = "externTypes" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + api 'org.apache.commons:commons-math3:3.6.1' +} \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_api/src/main/java/externTypes/externTypes_api/ExternTypesTestHelper.java b/goldenmaster/externTypes/externTypes_api/src/main/java/externTypes/externTypes_api/ExternTypesTestHelper.java new file mode 100644 index 0000000..7e93efa --- /dev/null +++ b/goldenmaster/externTypes/externTypes_api/src/main/java/externTypes/externTypes_api/ExternTypesTestHelper.java @@ -0,0 +1,9 @@ +package externTypes.externTypes_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class ExternTypesTestHelper +{ +} \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/build.gradle b/goldenmaster/externTypes/externTypes_client_example/build.gradle new file mode 100644 index 0000000..ac1c783 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "externTypes" +version = "1.0.0" + +android { + namespace 'externTypes.externTypes_client_example' + compileSdk 35 + + defaultConfig { + applicationId "externTypes.externTypes_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':externTypes_api') + implementation project(':externTypes_android_messenger') + implementation project(':externTypes_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/AndroidManifest.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cecdace --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/java/externTypes/externTypes_client_example/ExternTypesTestClientApp.java b/goldenmaster/externTypes/externTypes_client_example/src/main/java/externTypes/externTypes_client_example/ExternTypesTestClientApp.java new file mode 100644 index 0000000..a802152 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/java/externTypes/externTypes_client_example/ExternTypesTestClientApp.java @@ -0,0 +1,13 @@ +package externTypes.externTypes_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/values-night/themes.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/colors.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/strings.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..5fb3087 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + ExternTypesTestClientApp + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/themes.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypes_impl/additions.gradle b/goldenmaster/externTypes/externTypes_impl/additions.gradle new file mode 100644 index 0000000..3664620 --- /dev/null +++ b/goldenmaster/externTypes/externTypes_impl/additions.gradle @@ -0,0 +1,19 @@ + +android { + namespace 'externTypes.externTypes_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypes_impl/build.gradle b/goldenmaster/externTypes/externTypes_impl/build.gradle new file mode 100644 index 0000000..eea7f0d --- /dev/null +++ b/goldenmaster/externTypes/externTypes_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "externTypes" +version = "1.0.0" + +android { + namespace 'externTypes.externTypes_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':externTypes_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/externTypes/externTypesserviceexample/build.gradle b/goldenmaster/externTypes/externTypesserviceexample/build.gradle new file mode 100644 index 0000000..01aad35 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "externTypes" +version = "1.0.0" + + +android { + namespace 'externTypes.externTypesserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "externTypes.externTypesserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':externTypes_api') + implementation project(':externTypes_android_messenger') + implementation project(':externTypes_android_service') + implementation project(':externTypes_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/AndroidManifest.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..41be253 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/java/externTypes/externTypesserviceexample/ExternTypesTestServiceApp.java b/goldenmaster/externTypes/externTypesserviceexample/src/main/java/externTypes/externTypesserviceexample/ExternTypesTestServiceApp.java new file mode 100644 index 0000000..1347fb0 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/java/externTypes/externTypesserviceexample/ExternTypesTestServiceApp.java @@ -0,0 +1,14 @@ +package externTypes.externTypesserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/colors.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/strings.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..42618e4 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + ExternTypesTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/themes.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/externTypes/gradle.properties b/goldenmaster/externTypes/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/externTypes/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/externTypes/gradle/libs.versions.toml b/goldenmaster/externTypes/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/externTypes/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/externTypes/settings.gradle b/goldenmaster/externTypes/settings.gradle new file mode 100644 index 0000000..e5582ef --- /dev/null +++ b/goldenmaster/externTypes/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "externTypes" +include ':externTypes_android_service' +include ':externTypes_android_client' +include ':externTypes_android_messenger' +include ':externTypes_impl' +include ':externTypes_api' +include ':externTypes_client_example' +include ':externTypesserviceexample' \ No newline at end of file diff --git a/goldenmaster/goldenmaster_example/build.gradle b/goldenmaster/goldenmaster_example/build.gradle index 0296c5c..f24fcf9 100644 --- a/goldenmaster/goldenmaster_example/build.gradle +++ b/goldenmaster/goldenmaster_example/build.gradle @@ -53,5 +53,30 @@ dependencies { implementation("testbed1:testbed1_android_messenger:1.0.0") implementation("testbed1:testbed1_impl:1.0.0") implementation("testbed1:testbed1_api:1.0.0") + implementation("customTypes:customTypes_android_service:1.0.0") + implementation("customTypes:customTypes_android_client:1.0.0") + implementation("customTypes:customTypes_android_messenger:1.0.0") + implementation("customTypes:customTypes_impl:1.0.0") + implementation("customTypes:customTypes_api:1.0.0") + implementation("externTypes:externTypes_android_service:1.0.0") + implementation("externTypes:externTypes_android_client:1.0.0") + implementation("externTypes:externTypes_android_messenger:1.0.0") + implementation("externTypes:externTypes_impl:1.0.0") + implementation("externTypes:externTypes_api:1.0.0") + implementation("counter:counter_android_service:1.0.0") + implementation("counter:counter_android_client:1.0.0") + implementation("counter:counter_android_messenger:1.0.0") + implementation("counter:counter_impl:1.0.0") + implementation("counter:counter_api:1.0.0") + implementation("tbIfaceimport:tbIfaceimport_android_service:1.0.0") + implementation("tbIfaceimport:tbIfaceimport_android_client:1.0.0") + implementation("tbIfaceimport:tbIfaceimport_android_messenger:1.0.0") + implementation("tbIfaceimport:tbIfaceimport_impl:1.0.0") + implementation("tbIfaceimport:tbIfaceimport_api:1.0.0") + implementation("tbRefIfaces:tbRefIfaces_android_service:1.0.0") + implementation("tbRefIfaces:tbRefIfaces_android_client:1.0.0") + implementation("tbRefIfaces:tbRefIfaces_android_messenger:1.0.0") + implementation("tbRefIfaces:tbRefIfaces_impl:1.0.0") + implementation("tbRefIfaces:tbRefIfaces_api:1.0.0") } \ No newline at end of file diff --git a/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java b/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java index 0b5db5e..a29d82c 100644 --- a/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java +++ b/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java @@ -155,6 +155,41 @@ protected void onCreate(Bundle savedInstanceState) { sb.append("Made instance of testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter\n"); testbed1.testbed1_impl.StructArrayInterfaceService testbed1_structArrayInterface_local_impl = new testbed1.testbed1_impl.StructArrayInterfaceService(); sb.append("Made instance of testbed1.testbed1_impl.StructArrayInterfaceService\n"); + testbed1.testbed1_android_client.StructArray2InterfaceClient testbed1_structArray2Interface_client = new testbed1.testbed1_android_client.StructArray2InterfaceClient(this.getApplicationContext(), "conn_testbed1_structArray2Interface_client"); + sb.append("Made instance of testbed1.testbed1_android_client.StructArray2InterfaceClient\n"); + testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter testbed1_structArray2Interface_service = new testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter(); + sb.append("Made instance of testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter\n"); + testbed1.testbed1_impl.StructArray2InterfaceService testbed1_structArray2Interface_local_impl = new testbed1.testbed1_impl.StructArray2InterfaceService(); + sb.append("Made instance of testbed1.testbed1_impl.StructArray2InterfaceService\n"); + + + + counter.counter_android_client.CounterClient counter_counter_client = new counter.counter_android_client.CounterClient(this.getApplicationContext(), "conn_counter_counter_client"); + sb.append("Made instance of counter.counter_android_client.CounterClient\n"); + counter.counter_android_service.CounterServiceAdapter counter_counter_service = new counter.counter_android_service.CounterServiceAdapter(); + sb.append("Made instance of counter.counter_android_service.CounterServiceAdapter\n"); + counter.counter_impl.CounterService counter_counter_local_impl = new counter.counter_impl.CounterService(); + sb.append("Made instance of counter.counter_impl.CounterService\n"); + + tbIfaceimport.tbIfaceimport_android_client.EmptyIfClient tbIfaceimport_emptyIf_client = new tbIfaceimport.tbIfaceimport_android_client.EmptyIfClient(this.getApplicationContext(), "conn_tbIfaceimport_emptyIf_client"); + sb.append("Made instance of tbIfaceimport.tbIfaceimport_android_client.EmptyIfClient\n"); + tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter tbIfaceimport_emptyIf_service = new tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter(); + sb.append("Made instance of tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter\n"); + tbIfaceimport.tbIfaceimport_impl.EmptyIfService tbIfaceimport_emptyIf_local_impl = new tbIfaceimport.tbIfaceimport_impl.EmptyIfService(); + sb.append("Made instance of tbIfaceimport.tbIfaceimport_impl.EmptyIfService\n"); + + tbRefIfaces.tbRefIfaces_android_client.SimpleLocalIfClient tbRefIfaces_simpleLocalIf_client = new tbRefIfaces.tbRefIfaces_android_client.SimpleLocalIfClient(this.getApplicationContext(), "conn_tbRefIfaces_simpleLocalIf_client"); + sb.append("Made instance of tbRefIfaces.tbRefIfaces_android_client.SimpleLocalIfClient\n"); + tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter tbRefIfaces_simpleLocalIf_service = new tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter(); + sb.append("Made instance of tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter\n"); + tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService tbRefIfaces_simpleLocalIf_local_impl = new tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService(); + sb.append("Made instance of tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService\n"); + tbRefIfaces.tbRefIfaces_android_client.ParentIfClient tbRefIfaces_parentIf_client = new tbRefIfaces.tbRefIfaces_android_client.ParentIfClient(this.getApplicationContext(), "conn_tbRefIfaces_parentIf_client"); + sb.append("Made instance of tbRefIfaces.tbRefIfaces_android_client.ParentIfClient\n"); + tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter tbRefIfaces_parentIf_service = new tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter(); + sb.append("Made instance of tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter\n"); + tbRefIfaces.tbRefIfaces_impl.ParentIfService tbRefIfaces_parentIf_local_impl = new tbRefIfaces.tbRefIfaces_impl.ParentIfService(); + sb.append("Made instance of tbRefIfaces.tbRefIfaces_impl.ParentIfService\n"); // Show output on screen TextView tv = new TextView(this); diff --git a/goldenmaster/settings.gradle b/goldenmaster/settings.gradle index 07d4392..fa20611 100644 --- a/goldenmaster/settings.gradle +++ b/goldenmaster/settings.gradle @@ -23,6 +23,11 @@ includeBuild 'tbSame1' includeBuild 'tbSame2' includeBuild 'tbSimple' includeBuild 'testbed1' +includeBuild 'customTypes' +includeBuild 'externTypes' +includeBuild 'counter' +includeBuild 'tbIfaceimport' +includeBuild 'tbRefIfaces' dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) diff --git a/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle b/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle index 8f3f3f0..4b75571 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle +++ b/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbEnum_api') diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java index a5685a5..8fc6e2a 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java @@ -191,10 +191,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); Enum0 prop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); @@ -261,7 +258,8 @@ public void handleMessage(Message msg) case SIG_Sig0: { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); Enum0 param0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); onSig0(param0); @@ -270,7 +268,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); onSig1(param1); @@ -279,7 +278,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); onSig2(param2); @@ -288,7 +288,8 @@ public void handleMessage(Message msg) case SIG_Sig3: { Bundle data = msg.getData(); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); Enum3 param3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); onSig3(param3); @@ -297,7 +298,8 @@ public void handleMessage(Message msg) case RPC_Func0Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -315,7 +317,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -333,7 +336,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -351,7 +355,8 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java index 44a95b9..1590b0f 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java @@ -162,7 +162,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); } -//TODO do not add when a property is readonly @Test public void onReceiveprop0PropertyChangeTest() throws RemoteException { // Create and send message @@ -176,7 +175,7 @@ public void onReceiveprop0PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp0Changed(testprop0); } - + @Test public void setPropertyRequestprop0() { @@ -184,7 +183,6 @@ public void setPropertyRequestprop0() testedClient.setProp0(testprop0); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -195,7 +193,7 @@ public void setPropertyRequestprop0() Enum0 receivedprop0 = data.getParcelable("prop0", Enum0Parcelable.class).getEnum0(); assertEquals(receivedprop0, testprop0); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -209,7 +207,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } - + @Test public void setPropertyRequestprop1() { @@ -217,7 +215,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -228,7 +225,7 @@ public void setPropertyRequestprop1() Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -242,7 +239,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); } - + @Test public void setPropertyRequestprop2() { @@ -250,7 +247,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -261,7 +257,7 @@ public void setPropertyRequestprop2() Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); assertEquals(receivedprop2, testprop2); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop3PropertyChangeTest() throws RemoteException { // Create and send message @@ -275,7 +271,7 @@ public void onReceiveprop3PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); } - + @Test public void setPropertyRequestprop3() { @@ -283,7 +279,6 @@ public void setPropertyRequestprop3() testedClient.setProp3(testprop3); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -294,6 +289,7 @@ public void setPropertyRequestprop3() Enum3 receivedprop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); assertEquals(receivedprop3, testprop3); } + @Test public void whenNotifiedsig0() throws RemoteException { @@ -382,7 +378,8 @@ public void onfunc0Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(EnumInterfaceMessageType.RPC_Func0Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); Enum0 receivedparam0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); assertEquals(receivedparam0, testparam0); @@ -427,7 +424,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(EnumInterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); @@ -472,7 +470,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(EnumInterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); assertEquals(receivedparam2, testparam2); @@ -517,7 +516,8 @@ public void onfunc3Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(EnumInterfaceMessageType.RPC_Func3Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); Enum3 receivedparam3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); assertEquals(receivedparam3, testparam3); diff --git a/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceParcelable.java b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceParcelable.java new file mode 100644 index 0000000..c3fb176 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_messenger/src/main/java/tbEnum/tbEnum_android_messenger/EnumInterfaceParcelable.java @@ -0,0 +1,78 @@ +package tbEnum.tbEnum_android_messenger; + +import tbEnum.tbEnum_api.IEnumInterface; +import android.os.Parcel; +import android.os.Parcelable; +import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_api.Enum3; + + public class EnumInterfaceParcelable implements Parcelable { + + public IEnumInterface data; + + public EnumInterfaceParcelable(IEnumInterface data) { + this.data = data; + } + + public IEnumInterface getEnumInterface() + { + return data; + } + + protected EnumInterfaceParcelable(Parcel in) { + Enum0Parcelable l_parcelableprop0 = in.readParcelable(Enum0Parcelable.class.getClassLoader(), Enum0Parcelable.class); + data.setProp0(l_parcelableprop0 != null ? l_parcelableprop0.data : null); + Enum1Parcelable l_parcelableprop1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + Enum2Parcelable l_parcelableprop2 = in.readParcelable(Enum2Parcelable.class.getClassLoader(), Enum2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + Enum3Parcelable l_parcelableprop3 = in.readParcelable(Enum3Parcelable.class.getClassLoader(), Enum3Parcelable.class); + data.setProp3(l_parcelableprop3 != null ? l_parcelableprop3.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public EnumInterfaceParcelable createFromParcel(Parcel in) { + return new EnumInterfaceParcelable(in); + } + + @Override + public EnumInterfaceParcelable[] newArray(int size) { + return new EnumInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum0Parcelable(data.getProp0()), flags); + dest.writeParcelable(new Enum1Parcelable(data.getProp1()), flags); + dest.writeParcelable(new Enum2Parcelable(data.getProp2()), flags); + dest.writeParcelable(new Enum3Parcelable(data.getProp3()), flags); + + + } + public static EnumInterfaceParcelable[] wrapArray(IEnumInterface[] elements) { + if (elements == null) return null; + EnumInterfaceParcelable[] out = new EnumInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new EnumInterfaceParcelable(elements[i]); + } + return out; + } + + public static IEnumInterface[] unwrapArray(EnumInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IEnumInterface[] out = new IEnumInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnumInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle b/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle index d204e1e..5a00c6c 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle +++ b/goldenmaster/tbEnum/tbEnum_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbEnum_api') - implementation project(':tbEnum_impl') - implementation project(':tbEnum_android_messenger') + api project(':tbEnum_impl') + api project(':tbEnum_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/tbEnum/tbEnum_android_service/build.gradle b/goldenmaster/tbEnum/tbEnum_android_service/build.gradle index 2966915..b6426ec 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/build.gradle +++ b/goldenmaster/tbEnum/tbEnum_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "tbEnum" version = "1.0.0" - android { namespace 'tbEnum.tbEnum_android_service' compileSdk 35 diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java index 3409cec..edcfcfc 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java @@ -37,10 +37,11 @@ public class EnumInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static IEnumInterface mBackendService; private static IEnumInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +56,19 @@ public static IEnumInterface setService(IEnumInterfaceServiceFactory factory) { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(EnumInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +79,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(EnumInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +114,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(EnumInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(EnumInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(EnumInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -227,7 +252,8 @@ public void handleMessage(Message msg) case RPC_Func0Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum0 param0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); @@ -256,7 +282,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); @@ -285,7 +312,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum2 param2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); @@ -314,7 +342,8 @@ public void handleMessage(Message msg) case RPC_Func3Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum3 param3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java index ca3911c..b71b215 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java @@ -180,10 +180,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); Enum3 receivedprop3 = data.getParcelable("prop3", Enum3Parcelable.class).getEnum3(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); assertEquals(receivedprop0, initprop0); assertEquals(receivedprop1, initprop1); assertEquals(receivedprop2, initprop2); @@ -225,7 +223,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop0PropertyChangeTest() throws RemoteException { // Create and send message @@ -260,7 +257,6 @@ public void whenNotifiedprop0() assertEquals(receivedprop0, testprop0); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -295,7 +291,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -330,7 +325,6 @@ public void whenNotifiedprop2() assertEquals(receivedprop2, testprop2); } -//TODO do not add when a property is readonly @Test public void onReceiveprop3PropertyChangeTest() throws RemoteException { // Create and send message @@ -378,7 +372,8 @@ public void whenNotifiedsig0() assertEquals(EnumInterfaceMessageType.SIG_Sig0.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); Enum0 receivedparam0 = data.getParcelable("param0", Enum0Parcelable.class).getEnum0(); assertEquals(receivedparam0, testparam0); @@ -396,7 +391,8 @@ public void whenNotifiedsig1() assertEquals(EnumInterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); @@ -414,7 +410,8 @@ public void whenNotifiedsig2() assertEquals(EnumInterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); Enum2 receivedparam2 = data.getParcelable("param2", Enum2Parcelable.class).getEnum2(); assertEquals(receivedparam2, testparam2); @@ -432,7 +429,8 @@ public void whenNotifiedsig3() assertEquals(EnumInterfaceMessageType.SIG_Sig3.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); Enum3 receivedparam3 = data.getParcelable("param3", Enum3Parcelable.class).getEnum3(); assertEquals(receivedparam3, testparam3); diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java index 4914482..90214bf 100644 --- a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java @@ -2,7 +2,6 @@ import tbEnum.tbEnum_api.IEnumInterfaceEventListener; import tbEnum.tbEnum_api.IEnumInterface; -//TODO imported/extern modules import tbEnum.tbEnum_api.Enum0; import tbEnum.tbEnum_api.Enum1; import tbEnum.tbEnum_api.Enum2; diff --git a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java index c8f708b..b66f915 100644 --- a/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java @@ -4,8 +4,16 @@ import java.util.Objects; import java.util.Arrays; - public class TbEnumTestHelper { + static public IEnumInterface makeTestEnumInterface(IEnumInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setProp0(Enum0.Value1); + testObjToFill.setProp1(Enum1.Value2); + testObjToFill.setProp2(Enum2.Value1); + testObjToFill.setProp3(Enum3.Value2); + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_client_example/build.gradle b/goldenmaster/tbEnum/tbEnum_client_example/build.gradle index 8d0ebba..0e8a889 100644 --- a/goldenmaster/tbEnum/tbEnum_client_example/build.gradle +++ b/goldenmaster/tbEnum/tbEnum_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "tbEnum" +version = "1.0.0" + android { namespace 'tbEnum.tbEnum_client_example' compileSdk 35 diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java index 904d9aa..b48475a 100644 --- a/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java @@ -13,8 +13,6 @@ //TODO for each interface there coudl be a tab? now only first one is added import tbEnum.tbEnum_android_client.EnumInterfaceClient; - -//import message type and parcelabe types import tbEnum.tbEnum_api.Enum0; import tbEnum.tbEnum_android_messenger.Enum0Parcelable; import tbEnum.tbEnum_api.Enum1; @@ -23,7 +21,6 @@ import tbEnum.tbEnum_android_messenger.Enum2Parcelable; import tbEnum.tbEnum_api.Enum3; import tbEnum.tbEnum_android_messenger.Enum3Parcelable; - import tbEnum.tbEnum_api.IEnumInterfaceEventListener; import java.util.concurrent.CompletableFuture; @@ -355,4 +352,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/tbEnum/tbEnum_impl/additions.gradle b/goldenmaster/tbEnum/tbEnum_impl/additions.gradle index 84a2786..77b3727 100644 --- a/goldenmaster/tbEnum/tbEnum_impl/additions.gradle +++ b/goldenmaster/tbEnum/tbEnum_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'tbEnum.tbEnum_impl' diff --git a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java index 44a8f15..02e66d7 100644 --- a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java +++ b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java @@ -6,9 +6,13 @@ import tbEnum.tbEnum_android_client.EnumInterfaceClient; import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; import tbEnum.tbEnum_api.Enum3; +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java index be5c853..d6aefc0 100644 --- a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java @@ -7,10 +7,13 @@ import tbEnum.tbEnum_api.AbstractEnumInterface; import tbEnum.tbEnum_api.IEnumInterfaceEventListener; import tbEnum.tbEnum_api.Enum0; +import tbEnum.tbEnum_android_messenger.Enum0Parcelable; import tbEnum.tbEnum_api.Enum1; +import tbEnum.tbEnum_android_messenger.Enum1Parcelable; import tbEnum.tbEnum_api.Enum2; +import tbEnum.tbEnum_android_messenger.Enum2Parcelable; import tbEnum.tbEnum_api.Enum3; - +import tbEnum.tbEnum_android_messenger.Enum3Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle b/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle index 961cae6..809487a 100644 --- a/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle +++ b/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "tbEnum" +version = "1.0.0" + + android { namespace 'tbEnum.tbEnumserviceexample' compileSdk 35 diff --git a/goldenmaster/tbIfaceimport/gradle.properties b/goldenmaster/tbIfaceimport/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbIfaceimport/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/gradle/libs.versions.toml b/goldenmaster/tbIfaceimport/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbIfaceimport/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbIfaceimport/settings.gradle b/goldenmaster/tbIfaceimport/settings.gradle new file mode 100644 index 0000000..b3050e2 --- /dev/null +++ b/goldenmaster/tbIfaceimport/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbIfaceimport" +include ':tbIfaceimport_android_service' +include ':tbIfaceimport_android_client' +include ':tbIfaceimport_android_messenger' +include ':tbIfaceimport_impl' +include ':tbIfaceimport_api' +include ':tbIfaceimport_client_example' +include ':tbIfaceimportserviceexample' \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/additions.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/additions.gradle new file mode 100644 index 0000000..80a75d3 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbIfaceimport.tbIfaceimport_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + implementation project(':tbIfaceimport_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle new file mode 100644 index 0000000..7f9b59b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbIfaceimport" +version = "1.0.0" + +android { + namespace 'tbIfaceimport.tbIfaceimport_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + implementation project(':tbIfaceimport_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java new file mode 100644 index 0000000..a86148d --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java @@ -0,0 +1,205 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbIfaceimport.tbIfaceimport_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class EmptyIfClient extends AbstractEmptyIf implements ServiceConnection +{ + private static final String TAG = "EmptyIfClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + + + public EmptyIfClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type EmptyIfServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, EmptyIfMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, EmptyIfMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (EmptyIfMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + // methods + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java new file mode 100644 index 0000000..c253903 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java @@ -0,0 +1,146 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbIfaceimport.tbIfaceimport_android_client; + +import tbIfaceimport.tbIfaceimport_android_client.EmptyIfClient; + +//import message type and parcelabe types +import tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IEmptyIfClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class EmptyIfClientTest +{ + + @Mock + private Context mMockContext; + + private EmptyIfClient testedClient; + private IEmptyIfEventListener listenerMock = mock(IEmptyIfEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IEmptyIfClientMessageGetter serviceMessagesStorage = mock(IEmptyIfClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(EmptyIfMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IEmptyIfClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new EmptyIfClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbIfaceimport.tbIfaceimport_android_service", "tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(EmptyIfMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, EmptyIfMessageType.INIT.getValue()); + Bundle data = new Bundle(); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + } + +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/additions.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/additions.gradle new file mode 100644 index 0000000..d9caadf --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/additions.gradle @@ -0,0 +1,18 @@ +android { + namespace 'tbIfaceimport.tbIfaceimport_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/build.gradle new file mode 100644 index 0000000..28d1926 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbIfaceimport" +version = "1.0.0" + +android { + namespace 'tbIfaceimport.tbIfaceimport_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfMessageType.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfMessageType.java new file mode 100644 index 0000000..ba1aa58 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfMessageType.java @@ -0,0 +1,30 @@ +package tbIfaceimport.tbIfaceimport_android_messenger; + +public enum EmptyIfMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + EmptyIfMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + EmptyIfMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static EmptyIfMessageType fromInteger(int value) + { + for (EmptyIfMessageType event : EmptyIfMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return EmptyIfMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfParcelable.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfParcelable.java new file mode 100644 index 0000000..8962689 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_messenger/src/main/java/tbIfaceimport/tbIfaceimport_android_messenger/EmptyIfParcelable.java @@ -0,0 +1,62 @@ +package tbIfaceimport.tbIfaceimport_android_messenger; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import android.os.Parcel; +import android.os.Parcelable; + + public class EmptyIfParcelable implements Parcelable { + + public IEmptyIf data; + + public EmptyIfParcelable(IEmptyIf data) { + this.data = data; + } + + public IEmptyIf getEmptyIf() + { + return data; + } + + protected EmptyIfParcelable(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public EmptyIfParcelable createFromParcel(Parcel in) { + return new EmptyIfParcelable(in); + } + + @Override + public EmptyIfParcelable[] newArray(int size) { + return new EmptyIfParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + + } + public static EmptyIfParcelable[] wrapArray(IEmptyIf[] elements) { + if (elements == null) return null; + EmptyIfParcelable[] out = new EmptyIfParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new EmptyIfParcelable(elements[i]); + } + return out; + } + + public static IEmptyIf[] unwrapArray(EmptyIfParcelable[] parcelables) { + if (parcelables == null) return null; + IEmptyIf[] out = new IEmptyIf[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEmptyIf(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/additions.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/additions.gradle new file mode 100644 index 0000000..a68ac25 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/additions.gradle @@ -0,0 +1,20 @@ +android { + namespace 'tbIfaceimport.tbIfaceimport_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + api project(':tbIfaceimport_impl') + api project(':tbIfaceimport_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/build.gradle new file mode 100644 index 0000000..7f8d1e0 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/build.gradle @@ -0,0 +1,36 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbIfaceimport" +version = "1.0.0" + +android { + namespace 'tbIfaceimport.tbIfaceimport_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + implementation project(':tbIfaceimport_impl') + implementation project(':tbIfaceimport_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a18f26d --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java new file mode 100644 index 0000000..9755db5 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java @@ -0,0 +1,253 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbIfaceimport.tbIfaceimport_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_android_service.IEmptyIfServiceFactory; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class EmptyIfServiceAdapter extends Service +{ + private static final String TAG = "EmptyIfServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private static Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IEmptyIf mBackendService; + private static IEmptyIfServiceFactory mServiceFactory; + private static final Object mutex = new Object(); + + //private final List mMessagesQueue = new ArrayList<>(); + + public EmptyIfServiceAdapter() + { + } + + public static IEmptyIf setService(IEmptyIfServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + synchronized (mutex) + { + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(EmptyIf) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(EmptyIfService) called. context = " + this); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: EmptyIfService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + Log.i(TAG, "LIFECYCLE: onDestroy(EmptyIfService) - proc = " + ", mMessenger = " + mMessenger); + + if (mHandler != null) + { + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IEmptyIfEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (EmptyIfMessageType.fromInteger(msg.what) != EmptyIfMessageType.REGISTER_CLIENT + && EmptyIfMessageType.fromInteger(msg.what) != EmptyIfMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: EmptyIfMessageType" + EmptyIfMessageType.fromInteger(msg.what) ); + return; + } + } + switch (EmptyIfMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = EmptyIfMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java new file mode 100644 index 0000000..48a95e1 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbIfaceimport.tbIfaceimport_android_service; + +import tbIfaceimport.tbIfaceimport_android_service.IEmptyIfServiceFactory; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_impl.EmptyIfService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton EmptyIfServiceFactory thread for the system. This is a thread for + * EmptyIfServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class EmptyIfServiceFactory extends HandlerThread implements IEmptyIfServiceFactory +{ + private EmptyIfService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static EmptyIfServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: EmptyIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractEmptyIf getServiceInstance() + { + if (m_Service == null) + { + m_Service = new EmptyIfService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new EmptyIfService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final EmptyIfServiceFactory INSTANCE = createInstance(); + } + + private EmptyIfServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static EmptyIfServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + EmptyIfServiceFactory t = new EmptyIfServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java new file mode 100644 index 0000000..ecc6062 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java @@ -0,0 +1,42 @@ +package tbIfaceimport.tbIfaceimport_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter; +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbIfaceimport.tbIfaceimport_impl; . +public class EmptyIfServiceStarter { + + static Intent androidService = null; + private static final String TAG = "EmptyIfStarter"; + + + + public static IEmptyIf start(Context context) { + stop(context); + androidService = new Intent(context, EmptyIfServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + EmptyIfServiceFactory factory = EmptyIfServiceFactory.get(); + Log.w(TAG, "starter: factory set for EmptyIfServiceFactory"); + return EmptyIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/IEmptyIfServiceFactory.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/IEmptyIfServiceFactory.java new file mode 100644 index 0000000..f299274 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/IEmptyIfServiceFactory.java @@ -0,0 +1,7 @@ +package tbIfaceimport.tbIfaceimport_android_service; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; + + +public interface IEmptyIfServiceFactory { + public IEmptyIf getServiceInstance(); +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java new file mode 100644 index 0000000..24d9b7a --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java @@ -0,0 +1,199 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbIfaceimport.tbIfaceimport_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter; + +//import message type and parcelabe types +import tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper; + + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_android_service.IEmptyIfServiceFactory; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IEmptyIfMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class EmptyIfServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private EmptyIfServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IEmptyIfEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IEmptyIf backendServiceMock = mock(IEmptyIf.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IEmptyIfServiceFactory serviceFactory = mock(IEmptyIfServiceFactory.class); + private IEmptyIfMessageGetter clientMessagesStorage = mock(IEmptyIfMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, EmptyIfMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IEmptyIfMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + + + Message registerMsg = Message.obtain(null, EmptyIfMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(EmptyIfMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, EmptyIfServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(EmptyIfServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IEmptyIfEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void onlySetupAndTeardown() + { + + } + +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_api/additions.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_api/additions.gradle new file mode 100644 index 0000000..357b783 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_api/additions.gradle @@ -0,0 +1,7 @@ +android { + namespace 'tbIfaceimport.tbIfaceimport_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_api/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_api/build.gradle new file mode 100644 index 0000000..fc7e536 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_api/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' +} + +group = "tbIfaceimport" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/AbstractEmptyIf.java b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/AbstractEmptyIf.java new file mode 100644 index 0000000..d1a17cb --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/AbstractEmptyIf.java @@ -0,0 +1,24 @@ +package tbIfaceimport.tbIfaceimport_api; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractEmptyIf implements IEmptyIf { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IEmptyIfEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IEmptyIfEventListener listener) { + listeners.remove(listener); + } + + public void fire_readyStatusChanged(boolean isReady) + { + for (IEmptyIfEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIf.java b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIf.java new file mode 100644 index 0000000..29d8a0a --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIf.java @@ -0,0 +1,16 @@ +package tbIfaceimport.tbIfaceimport_api; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface IEmptyIf { + // properties + // methods + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IEmptyIfEventListener listener); + void removeEventListener(IEmptyIfEventListener listener); + } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIfEventListener.java b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIfEventListener.java new file mode 100644 index 0000000..1ca760a --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/IEmptyIfEventListener.java @@ -0,0 +1,5 @@ +package tbIfaceimport.tbIfaceimport_api; + + public interface IEmptyIfEventListener { + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/TbIfaceimportTestHelper.java b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/TbIfaceimportTestHelper.java new file mode 100644 index 0000000..2f5ea83 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_api/src/main/java/tbIfaceimport/tbIfaceimport_api/TbIfaceimportTestHelper.java @@ -0,0 +1,15 @@ +package tbIfaceimport.tbIfaceimport_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class TbIfaceimportTestHelper +{ + + static public IEmptyIf makeTestEmptyIf(IEmptyIf testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + return testObjToFill; + } +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/build.gradle new file mode 100644 index 0000000..14d8757 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbIfaceimport" +version = "1.0.0" + +android { + namespace 'tbIfaceimport.tbIfaceimport_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbIfaceimport.tbIfaceimport_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbIfaceimport_api') + implementation project(':tbIfaceimport_android_messenger') + implementation project(':tbIfaceimport_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d784ed5 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java new file mode 100644 index 0000000..e76f86c --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java @@ -0,0 +1,187 @@ +package tbIfaceimport.tbIfaceimport_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbIfaceimport.tbIfaceimport_android_client.EmptyIfClient; +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbIfaceimportTestClientApp extends Activity implements IEmptyIfEventListener +{ + + private static final String TAG = "TbIfaceimportTestClientApp"; + + private EmptyIfClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbIfaceimport.tbIfaceimportserviceexample.TbIfaceimportTestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new EmptyIfClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/colors.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/strings.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..2ad4ba1 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbIfaceimportTestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/themes.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_impl/additions.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/additions.gradle new file mode 100644 index 0000000..edf4405 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/additions.gradle @@ -0,0 +1,19 @@ + +android { + namespace 'tbIfaceimport.tbIfaceimport_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_impl/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/build.gradle new file mode 100644 index 0000000..98ac6a7 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbIfaceimport" +version = "1.0.0" + +android { + namespace 'tbIfaceimport.tbIfaceimport_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbIfaceimport_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java new file mode 100644 index 0000000..009b1b8 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java @@ -0,0 +1,41 @@ +package tbIfaceimport.tbIfaceimport_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class EmptyIfService extends AbstractEmptyIf { + + private final static String TAG = "EmptyIfService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public EmptyIfService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java new file mode 100644 index 0000000..a22bebf --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java @@ -0,0 +1,71 @@ +package tbIfaceimport.tbIfaceimportjniclient; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; + +import tbIfaceimport.tbIfaceimport_android_client.EmptyIfClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class EmptyIfJniClient extends AbstractEmptyIf implements IEmptyIfEventListener +{ + + private static final String TAG = "EmptyIfJniClient"; + + private EmptyIfClient mMessengerClient = null; + + + private static String ModuleName = "tbIfaceimport.tbIfaceimportjniservice.EmptyIfJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new EmptyIfClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java new file mode 100644 index 0000000..8bd66ea --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java @@ -0,0 +1,48 @@ +package tbIfaceimport.tbIfaceimportjniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class EmptyIfJniService extends AbstractEmptyIf { + + + private final static String TAG = "EmptyIfJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public EmptyIfJniService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + // methods + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java new file mode 100644 index 0000000..57a0e7b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbIfaceimport.tbIfaceimportjniservice; + +import tbIfaceimport.tbIfaceimport_android_service.IEmptyIfServiceFactory; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_api.AbstractEmptyIf; +import tbIfaceimport.tbIfaceimportjniservice.EmptyIfJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton EmptyIfJniServiceFactory thread for the system. This is a thread for + * EmptyIfJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class EmptyIfJniServiceFactory extends HandlerThread implements IEmptyIfServiceFactory +{ + private EmptyIfJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static EmptyIfJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: EmptyIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractEmptyIf getServiceInstance() + { + if (jniService == null) + { + jniService = new EmptyIfJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new EmptyIfJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final EmptyIfJniServiceFactory INSTANCE = createInstance(); + } + + private EmptyIfJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static EmptyIfJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + EmptyIfJniServiceFactory t = new EmptyIfJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java new file mode 100644 index 0000000..0589a27 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbIfaceimport.tbIfaceimportjniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter; +import tbIfaceimport.tbIfaceimportjniservice.EmptyIfJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class EmptyIfJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "EmptyIfJniStarter"; + + + + public static IEmptyIf start(Context context) { + stop(context); + androidService = new Intent(context, EmptyIfServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + EmptyIfJniServiceFactory factory = EmptyIfJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for EmptyIfJniServiceFactory"); + return EmptyIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/build.gradle new file mode 100644 index 0000000..9ac9f7d --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbIfaceimport" +version = "1.0.0" + + +android { + namespace 'tbIfaceimport.tbIfaceimportserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbIfaceimport.tbIfaceimportserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbIfaceimport_api') + implementation project(':tbIfaceimport_android_messenger') + implementation project(':tbIfaceimport_android_service') + implementation project(':tbIfaceimport_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..65c4751 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java new file mode 100644 index 0000000..d794121 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java @@ -0,0 +1,168 @@ +package tbIfaceimport.tbIfaceimportserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceAdapter; +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceFactory; +import tbIfaceimport.tbIfaceimport_android_service.EmptyIfServiceStarter; + +//import message type and parcelabe types + +import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_impl.EmptyIfService; +import java.util.concurrent.CompletableFuture; + + + +public class TbIfaceimportTestServiceApp extends Activity implements IEmptyIfEventListener +{ + + private static final String TAG = "TbIfaceimportTestServiceApp"; + static Intent stub_service = null; + + + private IEmptyIf mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, EmptyIfServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = EmptyIfServiceAdapter.setService(EmptyIfServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/colors.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/strings.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..ea7e51b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbIfaceimportTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/themes.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_android_client/additions.gradle b/goldenmaster/tbNames/tbNames_android_client/additions.gradle index 399e6cd..c23e20f 100644 --- a/goldenmaster/tbNames/tbNames_android_client/additions.gradle +++ b/goldenmaster/tbNames/tbNames_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbNames_api') diff --git a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java index 0c14ffb..7769f20 100644 --- a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java +++ b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java @@ -17,6 +17,8 @@ import android.util.Log; //import message type and parcelabe types +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_api.INamEs; @@ -50,6 +52,7 @@ public class NamEsClient extends AbstractNamEs implements ServiceConnection private boolean m_Switch = false; private int m_SOME_PROPERTY = 0; private int m_Some_Poperty2 = 0; + private EnumWithUnderScores m_enum_property = EnumWithUnderScores.FirstValue; public NamEsClient(Context applicationContext, String connectionId) @@ -182,6 +185,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); + data.setClassLoader(EnumWithUnderScoresParcelable.class.getClassLoader()); boolean Switch = data.getBoolean("Switch", false); @@ -192,6 +196,9 @@ public void handleMessage(Message msg) int Some_Poperty2 = data.getInt("Some_Poperty2", 0); onSomePoperty2(Some_Poperty2); + + EnumWithUnderScores enum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + onEnumProperty(enum_property); break; } @@ -225,12 +232,24 @@ public void handleMessage(Message msg) onSomePoperty2(Some_Poperty2); break; } + case SET_EnumProperty: + { + Bundle data = msg.getData(); + data.setClassLoader(EnumWithUnderScoresParcelable.class.getClassLoader()); + + + EnumWithUnderScores enum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + + onEnumProperty(enum_property); + break; + } // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. case SIG_SomeSignal: { Bundle data = msg.getData(); + boolean SOME_PARAM = data.getBoolean("SOME_PARAM", false); onSomeSignal(SOME_PARAM); @@ -239,6 +258,7 @@ public void handleMessage(Message msg) case SIG_SomeSignal2: { Bundle data = msg.getData(); + boolean Some_Param = data.getBoolean("Some_Param", false); onSomeSignal2(Some_Param); @@ -247,6 +267,7 @@ public void handleMessage(Message msg) case RPC_SomeFunctionResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -264,6 +285,7 @@ public void handleMessage(Message msg) case RPC_SomeFunction2Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -394,6 +416,42 @@ public int getSomePoperty2() } + @Override + public void setEnumProperty(EnumWithUnderScores enum_property) + { + Log.i(TAG, "request setEnumProperty called "+ enum_property); + if (m_enum_property != enum_property) + { + Message msg = new Message(); + msg.what = NamEsMessageType.PROP_EnumProperty.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(enum_property)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onEnumProperty(EnumWithUnderScores enum_property) + { + Log.i(TAG, "value received from service for EnumProperty "); + if (m_enum_property != enum_property) + { + m_enum_property = enum_property; + fireEnumPropertyChanged(enum_property); + } + + } + + @Override + public EnumWithUnderScores getEnumProperty() + { + Log.i(TAG, "request getEnumProperty called, returning local"); + return m_enum_property; + } + + // methods diff --git a/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java index f4d416f..19de423 100644 --- a/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java +++ b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java @@ -5,6 +5,8 @@ //import message type and parcelabe types import tbNames.tbNames_api.TbNamesTestHelper; +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_api.INamEs; @@ -142,6 +144,8 @@ public void onInitReceive() throws RemoteException data.putInt("SOME_PROPERTY", testSOME_PROPERTY); int testSome_Poperty2 = 1; data.putInt("Some_Poperty2", testSome_Poperty2); + EnumWithUnderScores testenum_property = EnumWithUnderScores.SecondValue; + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(testenum_property)); //setup mock expectations msg.setData(data); @@ -150,8 +154,8 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onSwitchChanged(testSwitch); inOrderEventListener.verify(listenerMock,times(1)).onSomePropertyChanged(testSOME_PROPERTY); inOrderEventListener.verify(listenerMock,times(1)).onSomePoperty2Changed(testSome_Poperty2); + inOrderEventListener.verify(listenerMock,times(1)).onEnumPropertyChanged(testenum_property); } -//TODO do not add when a property is readonly @Test public void onReceiveSwitchPropertyChangeTest() throws RemoteException { // Create and send message @@ -165,7 +169,7 @@ public void onReceiveSwitchPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onSwitchChanged(testSwitch); } - + @Test public void setPropertyRequestSwitch() { @@ -173,7 +177,6 @@ public void setPropertyRequestSwitch() testedClient.setSwitch(testSwitch); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -183,7 +186,7 @@ public void setPropertyRequestSwitch() boolean receivedSwitch = data.getBoolean("Switch", false); assertEquals(receivedSwitch, testSwitch); } -//TODO do not add when a property is readonly + @Test public void onReceiveSOME_PROPERTYPropertyChangeTest() throws RemoteException { // Create and send message @@ -197,7 +200,7 @@ public void onReceiveSOME_PROPERTYPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onSomePropertyChanged(testSOME_PROPERTY); } - + @Test public void setPropertyRequestSOME_PROPERTY() { @@ -205,7 +208,6 @@ public void setPropertyRequestSOME_PROPERTY() testedClient.setSomeProperty(testSOME_PROPERTY); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -215,7 +217,7 @@ public void setPropertyRequestSOME_PROPERTY() int receivedSOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); assertEquals(receivedSOME_PROPERTY, testSOME_PROPERTY); } -//TODO do not add when a property is readonly + @Test public void onReceiveSome_Poperty2PropertyChangeTest() throws RemoteException { // Create and send message @@ -229,7 +231,7 @@ public void onReceiveSome_Poperty2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onSomePoperty2Changed(testSome_Poperty2); } - + @Test public void setPropertyRequestSome_Poperty2() { @@ -237,7 +239,6 @@ public void setPropertyRequestSome_Poperty2() testedClient.setSomePoperty2(testSome_Poperty2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -247,6 +248,39 @@ public void setPropertyRequestSome_Poperty2() int receivedSome_Poperty2 = data.getInt("Some_Poperty2", 0); assertEquals(receivedSome_Poperty2, testSome_Poperty2); } + + @Test + public void onReceiveenum_propertyPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.SET_EnumProperty.getValue()); + Bundle data = new Bundle(); + EnumWithUnderScores testenum_property = EnumWithUnderScores.SecondValue; + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(testenum_property)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onEnumPropertyChanged(testenum_property); + } + + @Test + public void setPropertyRequestenum_property() + { + EnumWithUnderScores testenum_property = EnumWithUnderScores.SecondValue; + + testedClient.setEnumProperty(testenum_property); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.PROP_EnumProperty.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(EnumWithUnderScoresParcelable.class.getClassLoader()); + + EnumWithUnderScores receivedenum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + assertEquals(receivedenum_property, testenum_property); + } + @Test public void whenNotifiedSOME_SIGNAL() throws RemoteException { @@ -302,6 +336,7 @@ public void onSOME_FUNCTIONRequest() throws RemoteException { assertEquals(NamEsMessageType.RPC_SomeFunctionReq.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean receivedSOME_PARAM = data.getBoolean("SOME_PARAM", false); assertEquals(receivedSOME_PARAM, testSOME_PARAM); int returnedCallId = data.getInt("callId", -1); @@ -343,6 +378,7 @@ public void onSome_Function2Request() throws RemoteException { assertEquals(NamEsMessageType.RPC_SomeFunction2Req.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean receivedSome_Param = data.getBoolean("Some_Param", false); assertEquals(receivedSome_Param, testSome_Param); int returnedCallId = data.getInt("callId", -1); diff --git a/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/EnumWithUnderScoresParcelable.java b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/EnumWithUnderScoresParcelable.java new file mode 100644 index 0000000..ff24805 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/EnumWithUnderScoresParcelable.java @@ -0,0 +1,69 @@ +package tbNames.tbNames_android_messenger; + +import tbNames.tbNames_api.EnumWithUnderScores; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class EnumWithUnderScoresParcelable implements Parcelable { + + public EnumWithUnderScores data; + + public EnumWithUnderScoresParcelable(EnumWithUnderScores data) { + this.data = data; + } + + public EnumWithUnderScores getEnumWithUnderScores() + { + return data; + } + + protected EnumWithUnderScoresParcelable(Parcel in) { + int intValue = in.readInt(); + this.data = EnumWithUnderScores.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public EnumWithUnderScoresParcelable createFromParcel(Parcel in) { + return new EnumWithUnderScoresParcelable(in); + } + + @Override + public EnumWithUnderScoresParcelable[] newArray(int size) { + return new EnumWithUnderScoresParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static EnumWithUnderScoresParcelable[] wrapArray(EnumWithUnderScores[] enums) { + if (enums == null) return null; + EnumWithUnderScoresParcelable[] result = new EnumWithUnderScoresParcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new EnumWithUnderScoresParcelable(enums[i]); + } + return result; + } + + public static EnumWithUnderScores[] unwrapArray(EnumWithUnderScoresParcelable[] parcelables) { + if (parcelables == null) return null; + EnumWithUnderScores[] out = new EnumWithUnderScores[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnumWithUnderScores(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java index 70f5d8e..532c6ba 100644 --- a/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java +++ b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java @@ -10,12 +10,14 @@ public enum NamEsMessageType { SET_SomeProperty(6), PROP_SomePoperty2(7), SET_SomePoperty2(8), - SIG_SomeSignal(9), - SIG_SomeSignal2(10), - RPC_SomeFunctionReq(11), - RPC_SomeFunctionResp(12), - RPC_SomeFunction2Req(13), - RPC_SomeFunction2Resp(14), + PROP_EnumProperty(9), + SET_EnumProperty(10), + SIG_SomeSignal(11), + SIG_SomeSignal2(12), + RPC_SomeFunctionReq(13), + RPC_SomeFunctionResp(14), + RPC_SomeFunction2Req(15), + RPC_SomeFunction2Resp(16), NamEsMessageType_UNKNOWN(Integer.MAX_VALUE); private final int value; diff --git a/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsParcelable.java b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsParcelable.java new file mode 100644 index 0000000..4b7b6e6 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsParcelable.java @@ -0,0 +1,72 @@ +package tbNames.tbNames_android_messenger; + +import tbNames.tbNames_api.INamEs; +import android.os.Parcel; +import android.os.Parcelable; +import tbNames.tbNames_api.EnumWithUnderScores; + + public class NamEsParcelable implements Parcelable { + + public INamEs data; + + public NamEsParcelable(INamEs data) { + this.data = data; + } + + public INamEs getNamEs() + { + return data; + } + + protected NamEsParcelable(Parcel in) { + data.setSwitch(in.readBoolean()); + data.setSomeProperty(in.readInt()); + data.setSomePoperty2(in.readInt()); + EnumWithUnderScoresParcelable l_parcelableenumProperty = in.readParcelable(EnumWithUnderScoresParcelable.class.getClassLoader(), EnumWithUnderScoresParcelable.class); + data.setEnumProperty(l_parcelableenumProperty != null ? l_parcelableenumProperty.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NamEsParcelable createFromParcel(Parcel in) { + return new NamEsParcelable(in); + } + + @Override + public NamEsParcelable[] newArray(int size) { + return new NamEsParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(data.getSwitch()); + dest.writeInt(data.getSomeProperty()); + dest.writeInt(data.getSomePoperty2()); + dest.writeParcelable(new EnumWithUnderScoresParcelable(data.getEnumProperty()), flags); + + + } + public static NamEsParcelable[] wrapArray(INamEs[] elements) { + if (elements == null) return null; + NamEsParcelable[] out = new NamEsParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NamEsParcelable(elements[i]); + } + return out; + } + + public static INamEs[] unwrapArray(NamEsParcelable[] parcelables) { + if (parcelables == null) return null; + INamEs[] out = new INamEs[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNamEs(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbNames/tbNames_android_service/additions.gradle b/goldenmaster/tbNames/tbNames_android_service/additions.gradle index efbea79..5b610e4 100644 --- a/goldenmaster/tbNames/tbNames_android_service/additions.gradle +++ b/goldenmaster/tbNames/tbNames_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbNames_api') - implementation project(':tbNames_impl') - implementation project(':tbNames_android_messenger') + api project(':tbNames_impl') + api project(':tbNames_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/tbNames/tbNames_android_service/build.gradle b/goldenmaster/tbNames/tbNames_android_service/build.gradle index 30ac99d..93e36c6 100644 --- a/goldenmaster/tbNames/tbNames_android_service/build.gradle +++ b/goldenmaster/tbNames/tbNames_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "tbNames" version = "1.0.0" - android { namespace 'tbNames.tbNames_android_service' compileSdk 35 diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java index f7c30e5..b2bafaf 100644 --- a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java @@ -14,6 +14,8 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_android_service.INamEsServiceFactory; @@ -29,10 +31,11 @@ public class NamEsServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INamEs mBackendService; private static INamEsServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +50,19 @@ public static INamEs setService(INamEsServiceFactory factory) { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NamEs) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +73,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NamEsService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +108,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NamEsService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NamEsService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NamEsService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -201,12 +228,22 @@ public void handleMessage(Message msg) mBackendService.setSomePoperty2(Some_Poperty2); break; } + case PROP_EnumProperty: + { + Bundle data = msg.getData(); + data.setClassLoader(EnumWithUnderScoresParcelable.class.getClassLoader()); + + EnumWithUnderScores enum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + mBackendService.setEnumProperty(enum_property); + break; + } // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. case RPC_SomeFunctionReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean SOME_PARAM = data.getBoolean("SOME_PARAM", false); @@ -233,6 +270,7 @@ public void handleMessage(Message msg) case RPC_SomeFunction2Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean Some_Param = data.getBoolean("Some_Param", false); @@ -297,6 +335,9 @@ private void sendInit() int Some_Poperty2 = mBackendService.getSomePoperty2(); data.putInt("Some_Poperty2", Some_Poperty2); + EnumWithUnderScores enum_property = mBackendService.getEnumProperty(); + + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(enum_property)); msg.setData(data); sendMessageToClients(msg); } @@ -337,6 +378,18 @@ public void onSomePoperty2Changed(int Some_Poperty2){ sendMessageToClients(msg); } @Override + public void onEnumPropertyChanged(EnumWithUnderScores enum_property){ + Log.i(TAG, "New value for EnumProperty from backend" + enum_property); + + Message msg = new Message(); + msg.what = NamEsMessageType.SET_EnumProperty.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(enum_property)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override public void onSomeSignal(boolean SOME_PARAM){ Log.i(TAG, "New singal for SomeSignal = "+ " " + SOME_PARAM); Message msg = new Message(); diff --git a/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java index cbba108..f4a16a1 100644 --- a/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java +++ b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java @@ -18,6 +18,8 @@ //import message type and parcelabe types import tbNames.tbNames_api.TbNamesTestHelper; +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import tbNames.tbNames_api.INamEsEventListener; @@ -140,6 +142,8 @@ void registerFakeActivityClient(Messenger messenger, String id) when(backendServiceMock.getSomeProperty()).thenReturn(initSOME_PROPERTY); int initSome_Poperty2 = 1; when(backendServiceMock.getSomePoperty2()).thenReturn(initSome_Poperty2); + EnumWithUnderScores initenum_property = EnumWithUnderScores.SecondValue; + when(backendServiceMock.getEnumProperty()).thenReturn(initenum_property); Message registerMsg = Message.obtain(null, NamEsMessageType.REGISTER_CLIENT.ordinal()); @@ -155,6 +159,7 @@ void registerFakeActivityClient(Messenger messenger, String id) inOrderBackendService.verify(backendServiceMock, times(1)).getSwitch(); inOrderBackendService.verify(backendServiceMock, times(1)).getSomeProperty(); inOrderBackendService.verify(backendServiceMock, times(1)).getSomePoperty2(); + inOrderBackendService.verify(backendServiceMock, times(1)).getEnumProperty(); inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -167,9 +172,14 @@ void registerFakeActivityClient(Messenger messenger, String id) int receivedSOME_PROPERTY = data.getInt("SOME_PROPERTY", 0); int receivedSome_Poperty2 = data.getInt("Some_Poperty2", 0); + + EnumWithUnderScores receivedenum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + + data.setClassLoader(EnumWithUnderScoresParcelable.class.getClassLoader()); assertEquals(receivedSwitch, initSwitch); assertEquals(receivedSOME_PROPERTY, initSOME_PROPERTY); assertEquals(receivedSome_Poperty2, initSome_Poperty2); + assertEquals(receivedenum_property, initenum_property); } @@ -207,7 +217,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveSwitchPropertyChangeTest() throws RemoteException { // Create and send message @@ -242,7 +251,6 @@ public void whenNotifiedSwitch() assertEquals(receivedSwitch, testSwitch); } -//TODO do not add when a property is readonly @Test public void onReceiveSOME_PROPERTYPropertyChangeTest() throws RemoteException { // Create and send message @@ -277,7 +285,6 @@ public void whenNotifiedSOME_PROPERTY() assertEquals(receivedSOME_PROPERTY, testSOME_PROPERTY); } -//TODO do not add when a property is readonly @Test public void onReceiveSome_Poperty2PropertyChangeTest() throws RemoteException { // Create and send message @@ -313,6 +320,40 @@ public void whenNotifiedSome_Poperty2() assertEquals(receivedSome_Poperty2, testSome_Poperty2); } @Test + public void onReceiveenum_propertyPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NamEsMessageType.PROP_EnumProperty.getValue()); + Bundle data = new Bundle(); + EnumWithUnderScores testenum_property = EnumWithUnderScores.SecondValue; + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(testenum_property)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setEnumProperty(testenum_property); + + } + + @Test + public void whenNotifiedenum_property() + { + EnumWithUnderScores testenum_property = EnumWithUnderScores.SecondValue; + + testedAdapterAsEventListener.onEnumPropertyChanged(testenum_property); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NamEsMessageType.SET_EnumProperty.getValue(), response.what); + Bundle data = response.getData(); + + + EnumWithUnderScores receivedenum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + + assertEquals(receivedenum_property, testenum_property); + } + @Test public void whenNotifiedSOME_SIGNAL() { boolean testSOME_PARAM = true; @@ -326,6 +367,7 @@ public void whenNotifiedSOME_SIGNAL() assertEquals(NamEsMessageType.SIG_SomeSignal.getValue(), response.what); Bundle data = response.getData(); + boolean receivedSOME_PARAM = data.getBoolean("SOME_PARAM", false); assertEquals(receivedSOME_PARAM, testSOME_PARAM); } @@ -343,6 +385,7 @@ public void whenNotifiedSome_Signal2() assertEquals(NamEsMessageType.SIG_SomeSignal2.getValue(), response.what); Bundle data = response.getData(); + boolean receivedSome_Param = data.getBoolean("Some_Param", false); assertEquals(receivedSome_Param, testSome_Param); } diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java index 49de190..3bfc5e3 100644 --- a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java @@ -2,7 +2,7 @@ import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_api.INamEs; -//TODO imported/extern modules +import tbNames.tbNames_api.EnumWithUnderScores; import java.util.Collection; import java.util.HashSet; @@ -36,6 +36,13 @@ public void fireSomePoperty2Changed(int newValue) { } } + @Override + public void fireEnumPropertyChanged(EnumWithUnderScores newValue) { + for (INamEsEventListener listener : listeners) { + listener.onEnumPropertyChanged(newValue); + } + } + @Override public void fireSomeSignal(boolean SOME_PARAM) { for (INamEsEventListener listener : listeners) { diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/EnumWithUnderScores.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/EnumWithUnderScores.java new file mode 100644 index 0000000..49bd890 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/EnumWithUnderScores.java @@ -0,0 +1,30 @@ +package tbNames.tbNames_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum EnumWithUnderScores +{ + @JsonProperty("0") + FirstValue(0), + @JsonProperty("1") + SecondValue(1), + @JsonProperty("2") + ThirdValue(2); + + private final int value; + + EnumWithUnderScores(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static EnumWithUnderScores fromValue(int value) { + for (EnumWithUnderScores e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java index 1431d0a..ced64be 100644 --- a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java @@ -1,6 +1,7 @@ package tbNames.tbNames_api; import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.EnumWithUnderScores; import java.util.concurrent.CompletableFuture; @@ -19,6 +20,10 @@ public interface INamEs { int getSomePoperty2(); void fireSomePoperty2Changed(int newValue); + void setEnumProperty(EnumWithUnderScores enum_property); + EnumWithUnderScores getEnumProperty(); + void fireEnumPropertyChanged(EnumWithUnderScores newValue); + // methods void someFunction(boolean SOME_PARAM); CompletableFuture someFunctionAsync(boolean SOME_PARAM); diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java index fdd1058..45b0f93 100644 --- a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java @@ -1,9 +1,11 @@ package tbNames.tbNames_api; +import tbNames.tbNames_api.EnumWithUnderScores; public interface INamEsEventListener { void onSwitchChanged(boolean newValue); void onSomePropertyChanged(int newValue); void onSomePoperty2Changed(int newValue); + void onEnumPropertyChanged(EnumWithUnderScores newValue); void onSomeSignal(boolean SOME_PARAM); void onSomeSignal2(boolean Some_Param); void on_readyStatusChanged(boolean isReady); diff --git a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java index 30e05c1..c98199d 100644 --- a/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java @@ -4,8 +4,16 @@ import java.util.Objects; import java.util.Arrays; - public class TbNamesTestHelper { + static public INamEs makeTestNamEs(INamEs testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setSwitch(true); + testObjToFill.setSomeProperty(1); + testObjToFill.setSomePoperty2(1); + testObjToFill.setEnumProperty(EnumWithUnderScores.SecondValue); + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_client_example/build.gradle b/goldenmaster/tbNames/tbNames_client_example/build.gradle index f4ecb7f..ae09a94 100644 --- a/goldenmaster/tbNames/tbNames_client_example/build.gradle +++ b/goldenmaster/tbNames/tbNames_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "tbNames" +version = "1.0.0" + android { namespace 'tbNames.tbNames_client_example' compileSdk 35 diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java index 90c62b7..803acf5 100644 --- a/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java @@ -13,9 +13,8 @@ //TODO for each interface there coudl be a tab? now only first one is added import tbNames.tbNames_android_client.NamEsClient; - -//import message type and parcelabe types - +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import tbNames.tbNames_api.INamEsEventListener; import java.util.concurrent.CompletableFuture; @@ -96,6 +95,18 @@ protected void onCreate(Bundle savedInstanceState) { mClient.setSomePoperty2(newSomePoperty2); }); propertyButtonsLine.addView(bSomePoperty2); + Button bEnumProperty = new Button(this); + bEnumProperty.setText("Set enum_property"); + bEnumProperty.setBackgroundColor(Color.GREEN); + + bEnumProperty.setOnClickListener(v -> { + EnumWithUnderScores newEnumProperty = mClient.getEnumProperty(); + + //TODO increment + Log.i(TAG, "SET enum_property" + newEnumProperty); + mClient.setEnumProperty(newEnumProperty); + }); + propertyButtonsLine.addView(bEnumProperty); layout.addView(propertyButtonsLine); @@ -259,6 +270,12 @@ public void onSomePoperty2Changed(int newValue) Log.w(TAG, "Property from service: Some_Poperty2 " + newValue); } @Override + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + outputTextViewProp.setText("Property from service: enum_property " + newValue); + Log.w(TAG, "Property from service: enum_property " + newValue); + } + @Override public void onSomeSignal(boolean SOME_PARAM) { String text = "Signal SOME_SIGNAL "+ " " + SOME_PARAM; @@ -285,4 +302,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/tbNames/tbNames_impl/additions.gradle b/goldenmaster/tbNames/tbNames_impl/additions.gradle index 89405ea..10ecde4 100644 --- a/goldenmaster/tbNames/tbNames_impl/additions.gradle +++ b/goldenmaster/tbNames/tbNames_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'tbNames.tbNames_impl' diff --git a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java index e34c239..ec5a521 100644 --- a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java +++ b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java @@ -6,6 +6,7 @@ import tbNames.tbNames_api.INamEs; import tbNames.tbNames_api.AbstractNamEs; import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.EnumWithUnderScores; import java.util.Map; @@ -27,6 +28,7 @@ public class NamEsService extends AbstractNamEs { private boolean m_Switch = false; private int m_SOME_PROPERTY = 0; private int m_Some_Poperty2 = 0; + private EnumWithUnderScores m_enum_property = EnumWithUnderScores.FirstValue; public NamEsService() { @@ -92,6 +94,26 @@ public int getSomePoperty2() } + @Override + public void setEnumProperty(EnumWithUnderScores enum_property) + { + Log.i(TAG, "request setEnumProperty called "); + if (m_enum_property != enum_property) + { + m_enum_property = enum_property; + onEnumPropertyChanged(m_enum_property); + } + + } + + @Override + public EnumWithUnderScores getEnumProperty() + { + Log.i(TAG, "request getEnumProperty called,"); + return m_enum_property; + } + + // methods @Override @@ -141,6 +163,11 @@ private void onSomePoperty2Changed(int newValue) Log.i(TAG, "onSomePoperty2Changed, will pass notification to all listeners"); fireSomePoperty2Changed(newValue); } + private void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + Log.i(TAG, "onEnumPropertyChanged, will pass notification to all listeners"); + fireEnumPropertyChanged(newValue); + } public void onSomeSignal(boolean SOME_PARAM) { Log.i(TAG, "onSomeSignal, will pass notification to all listeners"); diff --git a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java index 93a8e27..c15f2a2 100644 --- a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java +++ b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java @@ -5,6 +5,8 @@ import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_android_client.NamEsClient; +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import android.content.Context; import android.os.Bundle; @@ -68,6 +70,19 @@ public int getSomePoperty2() return mMessengerClient.getSomePoperty2(); } + @Override + public void setEnumProperty(EnumWithUnderScores enum_property) + { + Log.i(TAG, "got request from ue, setEnumProperty" + (enum_property)); + mMessengerClient.setEnumProperty(enum_property); + } + @Override + public EnumWithUnderScores getEnumProperty() + { + Log.i(TAG, "got request from ue, getEnumProperty"); + return mMessengerClient.getEnumProperty(); + } + public void someFunction(boolean SOME_PARAM) { Log.v(TAG, "Blocking callsomeFunction - should not be used "); @@ -161,6 +176,12 @@ public void onSomePoperty2Changed(int newValue) nativeOnSomePoperty2Changed(newValue); } @Override + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnEnumPropertyChanged(newValue); + } + @Override public void onSomeSignal(boolean SOME_PARAM) { Log.w(TAG, "NOTIFICATION from messenger client Signal SOME_SIGNAL "+ " " + SOME_PARAM); @@ -175,6 +196,7 @@ public void onSomeSignal2(boolean Some_Param) private native void nativeOnSwitchChanged(boolean Switch); private native void nativeOnSomePropertyChanged(int SOME_PROPERTY); private native void nativeOnSomePoperty2Changed(int Some_Poperty2); + private native void nativeOnEnumPropertyChanged(EnumWithUnderScores enum_property); private native void nativeOnSomeSignal(boolean SOME_PARAM); private native void nativeOnSomeSignal2(boolean Some_Param); private native void nativeOnSomeFunctionResult(String callId); diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java index d58d8c8..dc5c8d5 100644 --- a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java @@ -6,7 +6,8 @@ import tbNames.tbNames_api.INamEs; import tbNames.tbNames_api.AbstractNamEs; import tbNames.tbNames_api.INamEsEventListener; - +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -74,6 +75,21 @@ public int getSomePoperty2() } + @Override + public void setEnumProperty(EnumWithUnderScores enum_property) + { + Log.i(TAG, "request setEnumProperty called, will call native "); + nativeSetEnumProperty(enum_property); + } + + @Override + public EnumWithUnderScores getEnumProperty() + { + Log.i(TAG, "request getEnumProperty called, will call native "); + return nativeGetEnumProperty(); + } + + // methods @Override @@ -117,6 +133,9 @@ public boolean _isReady() { private native void nativeSetSomePoperty2(int Some_Poperty2); private native int nativeGetSomePoperty2(); + private native void nativeSetEnumProperty(EnumWithUnderScores enum_property); + private native EnumWithUnderScores nativeGetEnumProperty(); + // methods private native void nativeSomeFunction(boolean SOME_PARAM); private native void nativeSomeFunction2(boolean Some_Param); @@ -142,6 +161,11 @@ public void onSomePoperty2Changed(int newValue) Log.i(TAG, "onSomePoperty2Changed, will pass notification to all listeners"); fireSomePoperty2Changed(newValue); } + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + Log.i(TAG, "onEnumPropertyChanged, will pass notification to all listeners"); + fireEnumPropertyChanged(newValue); + } public void onSomeSignal(boolean SOME_PARAM) { Log.i(TAG, "onSomeSignal, will pass notification to all listeners"); diff --git a/goldenmaster/tbNames/tbNamesserviceexample/build.gradle b/goldenmaster/tbNames/tbNamesserviceexample/build.gradle index 0b2ecbd..44724bf 100644 --- a/goldenmaster/tbNames/tbNamesserviceexample/build.gradle +++ b/goldenmaster/tbNames/tbNamesserviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "tbNames" +version = "1.0.0" + + android { namespace 'tbNames.tbNamesserviceexample' compileSdk 35 diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java index 5275c50..3ba3026 100644 --- a/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java @@ -18,6 +18,8 @@ import tbNames.tbNames_android_service.NamEsServiceStarter; //import message type and parcelabe types +import tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_api.INamEs; @@ -97,6 +99,17 @@ protected void onCreate(Bundle savedInstanceState) { mBackend.setSomePoperty2(newSomePoperty2); }); propertyButtonsLine.addView(bSomePoperty2); + Button bEnumProperty = new Button(this); + bEnumProperty.setText("Set enum_property"); + bEnumProperty.setBackgroundColor(Color.GREEN); + + bEnumProperty.setOnClickListener(v -> { + EnumWithUnderScores newEnumProperty = mBackend.getEnumProperty(); + //TODO increment + Log.i(TAG, "SET enum_property" + newEnumProperty); + mBackend.setEnumProperty(newEnumProperty); + }); + propertyButtonsLine.addView(bEnumProperty); layout.addView(propertyButtonsLine); @@ -223,6 +236,12 @@ public void onSomePoperty2Changed(int newValue) Log.w(TAG, "Property from service: Some_Poperty2 " + newValue); } @Override + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + outputTextViewProp.setText("Property from service: enum_property " + newValue); + Log.w(TAG, "Property from service: enum_property " + newValue); + } + @Override public void onSomeSignal(boolean SOME_PARAM) { String text = "Signal SOME_SIGNAL "+ " " + SOME_PARAM; diff --git a/goldenmaster/tbRefIfaces/gradle.properties b/goldenmaster/tbRefIfaces/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/goldenmaster/tbRefIfaces/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/gradle/libs.versions.toml b/goldenmaster/tbRefIfaces/gradle/libs.versions.toml new file mode 100644 index 0000000..5436cc4 --- /dev/null +++ b/goldenmaster/tbRefIfaces/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +agp = "8.5.2" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +filamentAndroid = "1.17.1" +constraintlayout = "2.2.1" +navigationFragment = "2.8.9" +navigationUi = "2.8.9" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +filament-android = { group = "com.google.ar.sceneform", name = "filament-android", version.ref = "filamentAndroid" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } + diff --git a/goldenmaster/tbRefIfaces/settings.gradle b/goldenmaster/tbRefIfaces/settings.gradle new file mode 100644 index 0000000..d625610 --- /dev/null +++ b/goldenmaster/tbRefIfaces/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "tbRefIfaces" +include ':tbRefIfaces_android_service' +include ':tbRefIfaces_android_client' +include ':tbRefIfaces_android_messenger' +include ':tbRefIfaces_impl' +include ':tbRefIfaces_api' +include ':tbRefIfaces_client_example' +include ':tbRefIfacesserviceexample' \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/additions.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/additions.gradle new file mode 100644 index 0000000..f15146d --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/additions.gradle @@ -0,0 +1,21 @@ +android { + namespace 'tbRefIfaces.tbRefIfaces_android_client' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + implementation project(':tbRefIfaces_android_messenger') + api project(':tbIfaceimport_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle new file mode 100644 index 0000000..582feff --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle @@ -0,0 +1,36 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbRefIfaces" +version = "1.0.0" + +android { + namespace 'tbRefIfaces.tbRefIfaces_android_client' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + implementation project(':tbRefIfaces_android_messenger') + api 'tbIfaceimport:tbIfaceimport_android_messenger:1.0.0' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java new file mode 100644 index 0000000..fd6d5c9 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java @@ -0,0 +1,721 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class ParentIfClient extends AbstractParentIf implements ServiceConnection +{ + private static final String TAG = "ParentIfClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private ISimpleLocalIf m_localIf = null; + private ISimpleLocalIf[] m_localIfList = new ISimpleLocalIf[]{}; + private tbIfaceimport.tbIfaceimport_api.IEmptyIf m_importedIf = null; + private tbIfaceimport.tbIfaceimport_api.IEmptyIf[] m_importedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}; + + + public ParentIfClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type ParentIfServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, ParentIfMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, ParentIfMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (ParentIfMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + + ISimpleLocalIf localIf = data.getParcelable("localIf", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + onLocalIf(localIf); + + ISimpleLocalIf[] localIfList = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("localIfList", SimpleLocalIfParcelable.class)); + onLocalIfList(localIfList); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf = data.getParcelable("importedIf", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + onImportedIf(importedIf); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + onImportedIfList(importedIfList); + + break; + } + case SET_LocalIf: + { + Bundle data = msg.getData(); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + + ISimpleLocalIf localIf = data.getParcelable("localIf", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + + onLocalIf(localIf); + break; + } + case SET_LocalIfList: + { + Bundle data = msg.getData(); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + + ISimpleLocalIf[] localIfList = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("localIfList", SimpleLocalIfParcelable.class)); + + onLocalIfList(localIfList); + break; + } + case SET_ImportedIf: + { + Bundle data = msg.getData(); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + + tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf = data.getParcelable("importedIf", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + + onImportedIf(importedIf); + break; + } + case SET_ImportedIfList: + { + Bundle data = msg.getData(); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + + onImportedIfList(importedIfList); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_LocalIfSignal: { + + Bundle data = msg.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf param = data.getParcelable("param", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + onLocalIfSignal(param); + break; + } + case SIG_LocalIfSignalList: { + + Bundle data = msg.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf[] param = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("param", SimpleLocalIfParcelable.class)); + onLocalIfSignalList(param); + break; + } + case SIG_ImportedIfSignal: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf param = data.getParcelable("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + onImportedIfSignal(param); + break; + } + case SIG_ImportedIfSignalList: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + onImportedIfSignalList(param); + break; + } + case RPC_LocalIfMethodResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ParentIfMessageType.RPC_LocalIfMethodResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_LocalIfMethodListResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ParentIfMessageType.RPC_LocalIfMethodListResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_ImportedIfMethodResp: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ParentIfMessageType.RPC_ImportedIfMethodResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_ImportedIfMethodListResp: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received ParentIfMessageType.RPC_ImportedIfMethodListResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setLocalIf(ISimpleLocalIf localIf) + { + Log.i(TAG, "request setLocalIf called "+ localIf); + if (! m_localIf.equals(localIf)) + { + Message msg = new Message(); + msg.what = ParentIfMessageType.PROP_LocalIf.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("localIf", new SimpleLocalIfParcelable(localIf)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onLocalIf(ISimpleLocalIf localIf) + { + Log.i(TAG, "value received from service for LocalIf "); + if (! m_localIf.equals(localIf)) + { + m_localIf = localIf; + fireLocalIfChanged(localIf); + } + + } + + @Override + public ISimpleLocalIf getLocalIf() + { + Log.i(TAG, "request getLocalIf called, returning local"); + return m_localIf; + } + + + @Override + public void setLocalIfList(ISimpleLocalIf[] localIfList) + { + Log.i(TAG, "request setLocalIfList called "+ localIfList); + if (! Arrays.equals(m_localIfList, localIfList)) + { + Message msg = new Message(); + msg.what = ParentIfMessageType.PROP_LocalIfList.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(localIfList)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onLocalIfList(ISimpleLocalIf[] localIfList) + { + Log.i(TAG, "value received from service for LocalIfList "); + if (! Arrays.equals(m_localIfList, localIfList)) + { + m_localIfList = localIfList; + fireLocalIfListChanged(localIfList); + } + + } + + @Override + public ISimpleLocalIf[] getLocalIfList() + { + Log.i(TAG, "request getLocalIfList called, returning local"); + return m_localIfList; + } + + + @Override + public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) + { + Log.i(TAG, "request setImportedIf called "+ importedIf); + if (! m_importedIf.equals(importedIf)) + { + Message msg = new Message(); + msg.what = ParentIfMessageType.PROP_ImportedIf.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(importedIf)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) + { + Log.i(TAG, "value received from service for ImportedIf "); + if (! m_importedIf.equals(importedIf)) + { + m_importedIf = importedIf; + fireImportedIfChanged(importedIf); + } + + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf getImportedIf() + { + Log.i(TAG, "request getImportedIf called, returning local"); + return m_importedIf; + } + + + @Override + public void setImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList) + { + Log.i(TAG, "request setImportedIfList called "+ importedIfList); + if (! Arrays.equals(m_importedIfList, importedIfList)) + { + Message msg = new Message(); + msg.what = ParentIfMessageType.PROP_ImportedIfList.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(importedIfList)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList) + { + Log.i(TAG, "value received from service for ImportedIfList "); + if (! Arrays.equals(m_importedIfList, importedIfList)) + { + m_importedIfList = importedIfList; + fireImportedIfListChanged(importedIfList); + } + + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList() + { + Log.i(TAG, "request getImportedIfList called, returning local"); + return m_importedIfList; + } + + + // methods + + + @Override + public ISimpleLocalIf localIfMethod(ISimpleLocalIf param) { + CompletableFuture resFuture = localIfMethodAsync(param); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture localIfMethodAsync(ISimpleLocalIf param) { + + Log.i(TAG, "Call on service localIfMethod "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.RPC_LocalIfMethodReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param", new SimpleLocalIfParcelable(param)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + ISimpleLocalIf result = bundle.getParcelable("result", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + Log.v(TAG, "resolve localIfMethod" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param) { + CompletableFuture resFuture = localIfMethodListAsync(param); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture localIfMethodListAsync(ISimpleLocalIf[] param) { + + Log.i(TAG, "Call on service localIfMethodList "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.RPC_LocalIfMethodListReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(param)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + ISimpleLocalIf[] result = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])bundle.getParcelableArray("result", SimpleLocalIfParcelable.class)); + Log.v(TAG, "resolve localIfMethodList" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + CompletableFuture resFuture = importedIfMethodAsync(param); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture importedIfMethodAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + + Log.i(TAG, "Call on service importedIfMethod "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.RPC_ImportedIfMethodReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(param)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + tbIfaceimport.tbIfaceimport_api.IEmptyIf result = bundle.getParcelable("result", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + Log.v(TAG, "resolve importedIfMethod" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + CompletableFuture resFuture = importedIfMethodListAsync(param); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture importedIfMethodListAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + + Log.i(TAG, "Call on service importedIfMethodList "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.RPC_ImportedIfMethodListReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(param)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] result = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])bundle.getParcelableArray("result", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + Log.v(TAG, "resolve importedIfMethodList" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onLocalIfSignal(ISimpleLocalIf param) + { + Log.i(TAG, "onLocalIfSignal received from service"); + fireLocalIfSignal(param); + } + public void onLocalIfSignalList(ISimpleLocalIf[] param) + { + Log.i(TAG, "onLocalIfSignalList received from service"); + fireLocalIfSignalList(param); + } + public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.i(TAG, "onImportedIfSignal received from service"); + fireImportedIfSignal(param); + } + public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.i(TAG, "onImportedIfSignalList received from service"); + fireImportedIfSignalList(param); + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java new file mode 100644 index 0000000..fc27bc0 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java @@ -0,0 +1,329 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class SimpleLocalIfClient extends AbstractSimpleLocalIf implements ServiceConnection +{ + private static final String TAG = "SimpleLocalIfClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private int m_intProperty = 0; + + + public SimpleLocalIfClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type SimpleLocalIfServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, SimpleLocalIfMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, SimpleLocalIfMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (SimpleLocalIfMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + + + int intProperty = data.getInt("intProperty", 0); + onIntProperty(intProperty); + + break; + } + case SET_IntProperty: + { + Bundle data = msg.getData(); + + + int intProperty = data.getInt("intProperty", 0); + + onIntProperty(intProperty); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_IntSignal: { + + Bundle data = msg.getData(); + + + int param = data.getInt("param", 0); + onIntSignal(param); + break; + } + case RPC_IntMethodResp: { + + Bundle data = msg.getData(); + + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleLocalIfMessageType.RPC_IntMethodResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setIntProperty(int intProperty) + { + Log.i(TAG, "request setIntProperty called "+ intProperty); + if (m_intProperty != intProperty) + { + Message msg = new Message(); + msg.what = SimpleLocalIfMessageType.PROP_IntProperty.getValue(); + Bundle data = new Bundle(); + + data.putInt("intProperty", intProperty); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onIntProperty(int intProperty) + { + Log.i(TAG, "value received from service for IntProperty "); + if (m_intProperty != intProperty) + { + m_intProperty = intProperty; + fireIntPropertyChanged(intProperty); + } + + } + + @Override + public int getIntProperty() + { + Log.i(TAG, "request getIntProperty called, returning local"); + return m_intProperty; + } + + + // methods + + + @Override + public int intMethod(int param) { + CompletableFuture resFuture = intMethodAsync(param); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture intMethodAsync(int param) { + + Log.i(TAG, "Call on service intMethod "+ " " + param); + Message msg = new Message(); + msg.what = SimpleLocalIfMessageType.RPC_IntMethodReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putInt("param", param); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + int result = bundle.getInt("result", 0); + Log.v(TAG, "resolve intMethod" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onIntSignal(int param) + { + Log.i(TAG, "onIntSignal received from service"); + fireIntSignal(param); + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java new file mode 100644 index 0000000..f0edf43 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java @@ -0,0 +1,572 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbRefIfaces.tbRefIfaces_android_client; + +import tbRefIfaces.tbRefIfaces_android_client.ParentIfClient; + +//import message type and parcelabe types +import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IParentIfClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class ParentIfClientTest +{ + + @Mock + private Context mMockContext; + + private ParentIfClient testedClient; + private IParentIfEventListener listenerMock = mock(IParentIfEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IParentIfClientMessageGetter serviceMessagesStorage = mock(IParentIfClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(ParentIfMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IParentIfClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new ParentIfClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbRefIfaces.tbRefIfaces_android_service", "tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(ParentIfMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, ParentIfMessageType.INIT.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); + ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testimportedIf)); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testimportedIfList)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onLocalIfChanged(any(ISimpleLocalIf.class)); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onLocalIfListChanged(any(ISimpleLocalIf[].class)); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onImportedIfChanged(any(tbIfaceimport.tbIfaceimport_api.IEmptyIf.class)); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onImportedIfListChanged(any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class)); + } + @Test + public void onReceivelocalIfPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.SET_LocalIf.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onLocalIfChanged(any(ISimpleLocalIf.class)); + } + + /* + @Test + public void setPropertyRequestlocalIf() + { + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + + testedClient.setLocalIf(testlocalIf); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.PROP_LocalIf.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf receivedlocalIf = data.getParcelable("localIf", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + assertEquals(receivedlocalIf, testlocalIf); + } + + */ + @Test + public void onReceivelocalIfListPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.SET_LocalIfList.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onLocalIfListChanged(any(ISimpleLocalIf[].class)); + } + + /* + @Test + public void setPropertyRequestlocalIfList() + { + ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + + testedClient.setLocalIfList(testlocalIfList); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.PROP_LocalIfList.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf[] receivedlocalIfList = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("localIfList", SimpleLocalIfParcelable.class)); + assertEquals(receivedlocalIfList, testlocalIfList); + } + + */ + @Test + public void onReceiveimportedIfPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.SET_ImportedIf.getValue()); + Bundle data = new Bundle(); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testimportedIf)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onImportedIfChanged(any(tbIfaceimport.tbIfaceimport_api.IEmptyIf.class)); + } + + /* + @Test + public void setPropertyRequestimportedIf() + { + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + + testedClient.setImportedIf(testimportedIf); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.PROP_ImportedIf.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf receivedimportedIf = data.getParcelable("importedIf", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + assertEquals(receivedimportedIf, testimportedIf); + } + + */ + @Test + public void onReceiveimportedIfListPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.SET_ImportedIfList.getValue()); + Bundle data = new Bundle(); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testimportedIfList)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + // Make sure test data is properly filled and in case of extern serialization is in place. + //inOrderEventListener.verify(listenerMock,times(1)).onImportedIfListChanged(any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class)); + } + + /* + @Test + public void setPropertyRequestimportedIfList() + { + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + + testedClient.setImportedIfList(testimportedIfList); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.PROP_ImportedIfList.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] receivedimportedIfList = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + assertEquals(receivedimportedIfList, testimportedIfList); + } + + */ + @Test + public void whenNotifiedlocalIfSignal() throws RemoteException + { + + Message msg = Message.obtain(null, ParentIfMessageType.SIG_LocalIfSignal.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + data.putParcelable("param", new SimpleLocalIfParcelable(testparam)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onLocalIfSignal( any(ISimpleLocalIf.class)); + +} + @Test + public void whenNotifiedlocalIfSignalList() throws RemoteException + { + + Message msg = Message.obtain(null, ParentIfMessageType.SIG_LocalIfSignalList.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(testparam)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onLocalIfSignalList( any(ISimpleLocalIf[].class)); + +} + @Test + public void whenNotifiedimportedIfSignal() throws RemoteException + { + + Message msg = Message.obtain(null, ParentIfMessageType.SIG_ImportedIfSignal.getValue()); + Bundle data = new Bundle(); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + data.putParcelable("param", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testparam)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onImportedIfSignal( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf.class)); + +} + @Test + public void whenNotifiedimportedIfSignalList() throws RemoteException + { + + Message msg = Message.obtain(null, ParentIfMessageType.SIG_ImportedIfSignalList.getValue()); + Bundle data = new Bundle(); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + data.putParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testparam)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onImportedIfSignalList( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class)); + +} + + + public void onlocalIfMethodRequest() throws RemoteException { + + // Execute method + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf expectedResult = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.localIfMethodAsync(testparam); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ParentIfMessageType.RPC_LocalIfMethodReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf receivedparam = data.getParcelable("param", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + assertEquals(receivedparam, testparam); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ParentIfMessageType.RPC_LocalIfMethodResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new SimpleLocalIfParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onlocalIfMethodListRequest() throws RemoteException { + + // Execute method + ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + ISimpleLocalIf[] expectedResult = new ISimpleLocalIf[1]; + expectedResult[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.localIfMethodListAsync(testparam); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ParentIfMessageType.RPC_LocalIfMethodListReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf[] receivedparam = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("param", SimpleLocalIfParcelable.class)); + assertEquals(receivedparam, testparam); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ParentIfMessageType.RPC_LocalIfMethodListResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", SimpleLocalIfParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onimportedIfMethodRequest() throws RemoteException { + + // Execute method + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf expectedResult = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.importedIfMethodAsync(testparam); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ParentIfMessageType.RPC_ImportedIfMethodReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf receivedparam = data.getParcelable("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + assertEquals(receivedparam, testparam); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ParentIfMessageType.RPC_ImportedIfMethodResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onimportedIfMethodListRequest() throws RemoteException { + + // Execute method + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] expectedResult = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + expectedResult[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.importedIfMethodListAsync(testparam); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(ParentIfMessageType.RPC_ImportedIfMethodListReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] receivedparam = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + assertEquals(receivedparam, testparam); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, ParentIfMessageType.RPC_ImportedIfMethodListResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java new file mode 100644 index 0000000..50f50d2 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java @@ -0,0 +1,241 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package tbRefIfaces.tbRefIfaces_android_client; + +import tbRefIfaces.tbRefIfaces_android_client.SimpleLocalIfClient; + +//import message type and parcelabe types +import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface ISimpleLocalIfClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SimpleLocalIfClientTest +{ + + @Mock + private Context mMockContext; + + private SimpleLocalIfClient testedClient; + private ISimpleLocalIfEventListener listenerMock = mock(ISimpleLocalIfEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private ISimpleLocalIfClientMessageGetter serviceMessagesStorage = mock(ISimpleLocalIfClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SimpleLocalIfMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(ISimpleLocalIfClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new SimpleLocalIfClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("tbRefIfaces.tbRefIfaces_android_service", "tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(SimpleLocalIfMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, SimpleLocalIfMessageType.INIT.getValue()); + Bundle data = new Bundle(); + int testintProperty = 1; + data.putInt("intProperty", testintProperty); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onIntPropertyChanged(testintProperty); + } + @Test + public void onReceiveintPropertyPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleLocalIfMessageType.SET_IntProperty.getValue()); + Bundle data = new Bundle(); + int testintProperty = 1; + data.putInt("intProperty", testintProperty); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onIntPropertyChanged(testintProperty); + } + + @Test + public void setPropertyRequestintProperty() + { + int testintProperty = 1; + + testedClient.setIntProperty(testintProperty); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleLocalIfMessageType.PROP_IntProperty.getValue(), response.what); + Bundle data = response.getData(); + + int receivedintProperty = data.getInt("intProperty", 0); + assertEquals(receivedintProperty, testintProperty); + } + + @Test + public void whenNotifiedintSignal() throws RemoteException + { + + Message msg = Message.obtain(null, SimpleLocalIfMessageType.SIG_IntSignal.getValue()); + Bundle data = new Bundle(); + int testparam = 1; + data.putInt("param", testparam); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onIntSignal(testparam); + +} + + + public void onintMethodRequest() throws RemoteException { + + // Execute method + int testparam = 1; + int expectedResult = 1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.intMethodAsync(testparam); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.intValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleLocalIfMessageType.RPC_IntMethodReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + + int receivedparam = data.getInt("param", 0); + assertEquals(receivedparam, testparam); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleLocalIfMessageType.RPC_IntMethodResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putInt("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/additions.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/additions.gradle new file mode 100644 index 0000000..8660dad --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/additions.gradle @@ -0,0 +1,19 @@ +android { + namespace 'tbRefIfaces.tbRefIfaces_android_messenger' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + api project(':tbIfaceimport_android_messenger') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/build.gradle new file mode 100644 index 0000000..c7d1780 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/build.gradle @@ -0,0 +1,31 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbRefIfaces" +version = "1.0.0" + +android { + namespace 'tbRefIfaces.tbRefIfaces_android_messenger' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' + api 'tbIfaceimport:tbIfaceimport_android_messenger:1.0.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfMessageType.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfMessageType.java new file mode 100644 index 0000000..1f27e2a --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfMessageType.java @@ -0,0 +1,50 @@ +package tbRefIfaces.tbRefIfaces_android_messenger; + +public enum ParentIfMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_LocalIf(3), + SET_LocalIf(4), + PROP_LocalIfList(5), + SET_LocalIfList(6), + PROP_ImportedIf(7), + SET_ImportedIf(8), + PROP_ImportedIfList(9), + SET_ImportedIfList(10), + SIG_LocalIfSignal(11), + SIG_LocalIfSignalList(12), + SIG_ImportedIfSignal(13), + SIG_ImportedIfSignalList(14), + RPC_LocalIfMethodReq(15), + RPC_LocalIfMethodResp(16), + RPC_LocalIfMethodListReq(17), + RPC_LocalIfMethodListResp(18), + RPC_ImportedIfMethodReq(19), + RPC_ImportedIfMethodResp(20), + RPC_ImportedIfMethodListReq(21), + RPC_ImportedIfMethodListResp(22), + ParentIfMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + ParentIfMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static ParentIfMessageType fromInteger(int value) + { + for (ParentIfMessageType event : ParentIfMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return ParentIfMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfParcelable.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfParcelable.java new file mode 100644 index 0000000..8c579f2 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/ParentIfParcelable.java @@ -0,0 +1,75 @@ +package tbRefIfaces.tbRefIfaces_android_messenger; + +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import android.os.Parcel; +import android.os.Parcelable; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; + + public class ParentIfParcelable implements Parcelable { + + public IParentIf data; + + public ParentIfParcelable(IParentIf data) { + this.data = data; + } + + public IParentIf getParentIf() + { + return data; + } + + protected ParentIfParcelable(Parcel in) { + SimpleLocalIfParcelable l_parcelablelocalIf = in.readParcelable(SimpleLocalIfParcelable.class.getClassLoader(), SimpleLocalIfParcelable.class); + data.setLocalIf(l_parcelablelocalIf != null ? l_parcelablelocalIf.data : null); + SimpleLocalIfParcelable[] l_parcelablelocalIfList = in.createTypedArray(SimpleLocalIfParcelable.CREATOR); + data.setLocalIfList(SimpleLocalIfParcelable.unwrapArray(l_parcelablelocalIfList)); + tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable l_parcelableimportedIf = in.readParcelable(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader(), tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class); + data.setImportedIf(l_parcelableimportedIf != null ? l_parcelableimportedIf.data : null); + tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[] l_parcelableimportedIfList = in.createTypedArray(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.CREATOR); + data.setImportedIfList(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray(l_parcelableimportedIfList)); + } + + public static final Creator CREATOR = new Creator() { + @Override + public ParentIfParcelable createFromParcel(Parcel in) { + return new ParentIfParcelable(in); + } + + @Override + public ParentIfParcelable[] newArray(int size) { + return new ParentIfParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new SimpleLocalIfParcelable(data.getLocalIf()), flags); + dest.writeTypedArray(SimpleLocalIfParcelable.wrapArray(data.getLocalIfList()), flags); + dest.writeParcelable(new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(data.getImportedIf()), flags); + dest.writeTypedArray(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(data.getImportedIfList()), flags); + + + } + public static ParentIfParcelable[] wrapArray(IParentIf[] elements) { + if (elements == null) return null; + ParentIfParcelable[] out = new ParentIfParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new ParentIfParcelable(elements[i]); + } + return out; + } + + public static IParentIf[] unwrapArray(ParentIfParcelable[] parcelables) { + if (parcelables == null) return null; + IParentIf[] out = new IParentIf[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getParentIf(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfMessageType.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfMessageType.java new file mode 100644 index 0000000..57b0ef4 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfMessageType.java @@ -0,0 +1,35 @@ +package tbRefIfaces.tbRefIfaces_android_messenger; + +public enum SimpleLocalIfMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_IntProperty(3), + SET_IntProperty(4), + SIG_IntSignal(5), + RPC_IntMethodReq(6), + RPC_IntMethodResp(7), + SimpleLocalIfMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + SimpleLocalIfMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static SimpleLocalIfMessageType fromInteger(int value) + { + for (SimpleLocalIfMessageType event : SimpleLocalIfMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return SimpleLocalIfMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfParcelable.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfParcelable.java new file mode 100644 index 0000000..082145b --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_messenger/src/main/java/tbRefIfaces/tbRefIfaces_android_messenger/SimpleLocalIfParcelable.java @@ -0,0 +1,64 @@ +package tbRefIfaces.tbRefIfaces_android_messenger; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import android.os.Parcel; +import android.os.Parcelable; + + public class SimpleLocalIfParcelable implements Parcelable { + + public ISimpleLocalIf data; + + public SimpleLocalIfParcelable(ISimpleLocalIf data) { + this.data = data; + } + + public ISimpleLocalIf getSimpleLocalIf() + { + return data; + } + + protected SimpleLocalIfParcelable(Parcel in) { + data.setIntProperty(in.readInt()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SimpleLocalIfParcelable createFromParcel(Parcel in) { + return new SimpleLocalIfParcelable(in); + } + + @Override + public SimpleLocalIfParcelable[] newArray(int size) { + return new SimpleLocalIfParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.getIntProperty()); + + + } + public static SimpleLocalIfParcelable[] wrapArray(ISimpleLocalIf[] elements) { + if (elements == null) return null; + SimpleLocalIfParcelable[] out = new SimpleLocalIfParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SimpleLocalIfParcelable(elements[i]); + } + return out; + } + + public static ISimpleLocalIf[] unwrapArray(SimpleLocalIfParcelable[] parcelables) { + if (parcelables == null) return null; + ISimpleLocalIf[] out = new ISimpleLocalIf[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSimpleLocalIf(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/additions.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/additions.gradle new file mode 100644 index 0000000..cbab43c --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/additions.gradle @@ -0,0 +1,22 @@ +android { + namespace 'tbRefIfaces.tbRefIfaces_android_service' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + api project(':tbRefIfaces_impl') + api project(':tbRefIfaces_android_messenger') + api project(':tbIfaceimport_android_messenger') + api project(':tbIfaceimport_impl') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/build.gradle new file mode 100644 index 0000000..418eab8 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/build.gradle @@ -0,0 +1,38 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbRefIfaces" +version = "1.0.0" + +android { + namespace 'tbRefIfaces.tbRefIfaces_android_service' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + testOptions { + unitTests.includeAndroidResources = true + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + implementation project(':tbRefIfaces_impl') + implementation project(':tbRefIfaces_android_messenger') + api 'tbIfaceimport:tbIfaceimport_android_messenger:1.0.0' + implementation 'tbIfaceimport:tbIfaceimport_impl:1.0.0' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/AndroidManifest.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6fff8fd --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/IParentIfServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/IParentIfServiceFactory.java new file mode 100644 index 0000000..53728ec --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/IParentIfServiceFactory.java @@ -0,0 +1,7 @@ +package tbRefIfaces.tbRefIfaces_android_service; +import tbRefIfaces.tbRefIfaces_api.IParentIf; + + +public interface IParentIfServiceFactory { + public IParentIf getServiceInstance(); +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ISimpleLocalIfServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ISimpleLocalIfServiceFactory.java new file mode 100644 index 0000000..accec50 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ISimpleLocalIfServiceFactory.java @@ -0,0 +1,7 @@ +package tbRefIfaces.tbRefIfaces_android_service; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; + + +public interface ISimpleLocalIfServiceFactory { + public ISimpleLocalIf getServiceInstance(); +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java new file mode 100644 index 0000000..1a84fbc --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java @@ -0,0 +1,517 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_android_service.IParentIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class ParentIfServiceAdapter extends Service +{ + private static final String TAG = "ParentIfServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private static Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IParentIf mBackendService; + private static IParentIfServiceFactory mServiceFactory; + private static final Object mutex = new Object(); + + //private final List mMessagesQueue = new ArrayList<>(); + + public ParentIfServiceAdapter() + { + } + + public static IParentIf setService(IParentIfServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + synchronized (mutex) + { + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(ParentIf) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(ParentIfService) called. context = " + this); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: ParentIfService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + Log.i(TAG, "LIFECYCLE: onDestroy(ParentIfService) - proc = " + ", mMessenger = " + mMessenger); + + if (mHandler != null) + { + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IParentIfEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (ParentIfMessageType.fromInteger(msg.what) != ParentIfMessageType.REGISTER_CLIENT + && ParentIfMessageType.fromInteger(msg.what) != ParentIfMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: ParentIfMessageType" + ParentIfMessageType.fromInteger(msg.what) ); + return; + } + } + switch (ParentIfMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_LocalIf: + { + Bundle data = msg.getData(); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf localIf = data.getParcelable("localIf", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + mBackendService.setLocalIf(localIf); + break; + } + case PROP_LocalIfList: + { + Bundle data = msg.getData(); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf[] localIfList = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("localIfList", SimpleLocalIfParcelable.class)); + mBackendService.setLocalIfList(localIfList); + break; + } + case PROP_ImportedIf: + { + Bundle data = msg.getData(); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf = data.getParcelable("importedIf", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + mBackendService.setImportedIf(importedIf); + break; + } + case PROP_ImportedIfList: + { + Bundle data = msg.getData(); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + mBackendService.setImportedIfList(importedIfList); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_LocalIfMethodReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + ISimpleLocalIf param = data.getParcelable("param", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + + ISimpleLocalIf result = mBackendService.localIfMethod(param); + + Message respMsg = new Message(); + respMsg.what = ParentIfMessageType.RPC_LocalIfMethodResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new SimpleLocalIfParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_LocalIfMethodListReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + ISimpleLocalIf[] param = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("param", SimpleLocalIfParcelable.class)); + + ISimpleLocalIf[] result = mBackendService.localIfMethodList(param); + + Message respMsg = new Message(); + respMsg.what = ParentIfMessageType.RPC_LocalIfMethodListResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",SimpleLocalIfParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_ImportedIfMethodReq: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf param = data.getParcelable("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf result = mBackendService.importedIfMethod(param); + + Message respMsg = new Message(); + respMsg.what = ParentIfMessageType.RPC_ImportedIfMethodResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_ImportedIfMethodListReq: { + + Bundle data = msg.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] result = mBackendService.importedIfMethodList(param); + + Message respMsg = new Message(); + respMsg.what = ParentIfMessageType.RPC_ImportedIfMethodListResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = ParentIfMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + ISimpleLocalIf localIf = mBackendService.getLocalIf(); + + data.putParcelable("localIf", new SimpleLocalIfParcelable(localIf)); + ISimpleLocalIf[] localIfList = mBackendService.getLocalIfList(); + + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(localIfList)); + tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf = mBackendService.getImportedIf(); + + data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(importedIf)); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList = mBackendService.getImportedIfList(); + + data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(importedIfList)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onLocalIfChanged(ISimpleLocalIf localIf){ + Log.i(TAG, "New value for LocalIf from backend" + localIf); + + Message msg = new Message(); + msg.what = ParentIfMessageType.SET_LocalIf.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("localIf", new SimpleLocalIfParcelable(localIf)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onLocalIfListChanged(ISimpleLocalIf[] localIfList){ + Log.i(TAG, "New value for LocalIfList from backend" + localIfList); + + Message msg = new Message(); + msg.what = ParentIfMessageType.SET_LocalIfList.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(localIfList)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf){ + Log.i(TAG, "New value for ImportedIf from backend" + importedIf); + + Message msg = new Message(); + msg.what = ParentIfMessageType.SET_ImportedIf.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(importedIf)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList){ + Log.i(TAG, "New value for ImportedIfList from backend" + importedIfList); + + Message msg = new Message(); + msg.what = ParentIfMessageType.SET_ImportedIfList.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(importedIfList)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onLocalIfSignal(ISimpleLocalIf param){ + Log.i(TAG, "New singal for LocalIfSignal = "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.SIG_LocalIfSignal.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param", new SimpleLocalIfParcelable(param)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onLocalIfSignalList(ISimpleLocalIf[] param){ + Log.i(TAG, "New singal for LocalIfSignalList = "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.SIG_LocalIfSignalList.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(param)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param){ + Log.i(TAG, "New singal for ImportedIfSignal = "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.SIG_ImportedIfSignal.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("param", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(param)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param){ + Log.i(TAG, "New singal for ImportedIfSignalList = "+ " " + param); + Message msg = new Message(); + msg.what = ParentIfMessageType.SIG_ImportedIfSignalList.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(param)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java new file mode 100644 index 0000000..c9576c5 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_service; + +import tbRefIfaces.tbRefIfaces_android_service.IParentIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_impl.ParentIfService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton ParentIfServiceFactory thread for the system. This is a thread for + * ParentIfServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class ParentIfServiceFactory extends HandlerThread implements IParentIfServiceFactory +{ + private ParentIfService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static ParentIfServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: ParentIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractParentIf getServiceInstance() + { + if (m_Service == null) + { + m_Service = new ParentIfService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new ParentIfService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final ParentIfServiceFactory INSTANCE = createInstance(); + } + + private ParentIfServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static ParentIfServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + ParentIfServiceFactory t = new ParentIfServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java new file mode 100644 index 0000000..aa11b0f --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java @@ -0,0 +1,42 @@ +package tbRefIfaces.tbRefIfaces_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter; +import tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbRefIfaces.tbRefIfaces_impl; . +public class ParentIfServiceStarter { + + static Intent androidService = null; + private static final String TAG = "ParentIfStarter"; + + + + public static IParentIf start(Context context) { + stop(context); + androidService = new Intent(context, ParentIfServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + ParentIfServiceFactory factory = ParentIfServiceFactory.get(); + Log.w(TAG, "starter: factory set for ParentIfServiceFactory"); + return ParentIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java new file mode 100644 index 0000000..bf484f7 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java @@ -0,0 +1,316 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_android_service.ISimpleLocalIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class SimpleLocalIfServiceAdapter extends Service +{ + private static final String TAG = "SimpleLocalIfServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private static Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static ISimpleLocalIf mBackendService; + private static ISimpleLocalIfServiceFactory mServiceFactory; + private static final Object mutex = new Object(); + + //private final List mMessagesQueue = new ArrayList<>(); + + public SimpleLocalIfServiceAdapter() + { + } + + public static ISimpleLocalIf setService(ISimpleLocalIfServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + synchronized (mutex) + { + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SimpleLocalIf) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SimpleLocalIfService) called. context = " + this); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: SimpleLocalIfService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleLocalIfService) - proc = " + ", mMessenger = " + mMessenger); + + if (mHandler != null) + { + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements ISimpleLocalIfEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (SimpleLocalIfMessageType.fromInteger(msg.what) != SimpleLocalIfMessageType.REGISTER_CLIENT + && SimpleLocalIfMessageType.fromInteger(msg.what) != SimpleLocalIfMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: SimpleLocalIfMessageType" + SimpleLocalIfMessageType.fromInteger(msg.what) ); + return; + } + } + switch (SimpleLocalIfMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_IntProperty: + { + Bundle data = msg.getData(); + + int intProperty = data.getInt("intProperty", 0); + mBackendService.setIntProperty(intProperty); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_IntMethodReq: { + + Bundle data = msg.getData(); + + int callId = data.getInt("callId"); + + int param = data.getInt("param", 0); + + int result = mBackendService.intMethod(param); + + Message respMsg = new Message(); + respMsg.what = SimpleLocalIfMessageType.RPC_IntMethodResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putInt("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = SimpleLocalIfMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + int intProperty = mBackendService.getIntProperty(); + + data.putInt("intProperty", intProperty); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onIntPropertyChanged(int intProperty){ + Log.i(TAG, "New value for IntProperty from backend" + intProperty); + + Message msg = new Message(); + msg.what = SimpleLocalIfMessageType.SET_IntProperty.getValue(); + Bundle data = new Bundle(); + + data.putInt("intProperty", intProperty); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onIntSignal(int param){ + Log.i(TAG, "New singal for IntSignal = "+ " " + param); + Message msg = new Message(); + msg.what = SimpleLocalIfMessageType.SIG_IntSignal.getValue(); + Bundle data = new Bundle(); + + data.putInt("param", param); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java new file mode 100644 index 0000000..792436c --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_service; + +import tbRefIfaces.tbRefIfaces_android_service.ISimpleLocalIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SimpleLocalIfServiceFactory thread for the system. This is a thread for + * SimpleLocalIfServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SimpleLocalIfServiceFactory extends HandlerThread implements ISimpleLocalIfServiceFactory +{ + private SimpleLocalIfService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SimpleLocalIfServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SimpleLocalIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSimpleLocalIf getServiceInstance() + { + if (m_Service == null) + { + m_Service = new SimpleLocalIfService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SimpleLocalIfService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final SimpleLocalIfServiceFactory INSTANCE = createInstance(); + } + + private SimpleLocalIfServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SimpleLocalIfServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SimpleLocalIfServiceFactory t = new SimpleLocalIfServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java new file mode 100644 index 0000000..2106d16 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java @@ -0,0 +1,42 @@ +package tbRefIfaces.tbRefIfaces_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter; +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package tbRefIfaces.tbRefIfaces_impl; . +public class SimpleLocalIfServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SimpleLocalIfStarter"; + + + + public static ISimpleLocalIf start(Context context) { + stop(context); + androidService = new Intent(context, SimpleLocalIfServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SimpleLocalIfServiceFactory factory = SimpleLocalIfServiceFactory.get(); + Log.w(TAG, "starter: factory set for SimpleLocalIfServiceFactory"); + return SimpleLocalIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java new file mode 100644 index 0000000..d0eee18 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java @@ -0,0 +1,586 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter; + +//import message type and parcelabe types +import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; + + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_android_service.IParentIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IParentIfMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class ParentIfServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private ParentIfServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IParentIfEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IParentIf backendServiceMock = mock(IParentIf.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IParentIfServiceFactory serviceFactory = mock(IParentIfServiceFactory.class); + private IParentIfMessageGetter clientMessagesStorage = mock(IParentIfMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, ParentIfMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IParentIfMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + ISimpleLocalIf initlocalIf = new SimpleLocalIfService(); + //TODO fill fields + when(backendServiceMock.getLocalIf()).thenReturn(initlocalIf); + ISimpleLocalIf init_elementlocalIfList = new SimpleLocalIfService(); + // todo fill if is struct + ISimpleLocalIf[] initlocalIfList = new ISimpleLocalIf[]{ init_elementlocalIfList } ; + when(backendServiceMock.getLocalIfList()).thenReturn(initlocalIfList); + tbIfaceimport.tbIfaceimport_api.IEmptyIf initimportedIf = new tbIfaceimport.tbIfaceimport_impl.EmptyIfService(); + //TODO fill fields + when(backendServiceMock.getImportedIf()).thenReturn(initimportedIf); + tbIfaceimport.tbIfaceimport_api.IEmptyIf init_elementimportedIfList = new tbIfaceimport.tbIfaceimport_impl.EmptyIfService(); + // todo fill if is struct + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] initimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{ init_elementimportedIfList } ; + when(backendServiceMock.getImportedIfList()).thenReturn(initimportedIfList); + + + Message registerMsg = Message.obtain(null, ParentIfMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getLocalIf(); + inOrderBackendService.verify(backendServiceMock, times(1)).getLocalIfList(); + inOrderBackendService.verify(backendServiceMock, times(1)).getImportedIf(); + inOrderBackendService.verify(backendServiceMock, times(1)).getImportedIfList(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + ISimpleLocalIf receivedlocalIf = data.getParcelable("localIf", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + + ISimpleLocalIf[] receivedlocalIfList = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("localIfList", SimpleLocalIfParcelable.class)); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf receivedimportedIf = data.getParcelable("importedIf", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] receivedimportedIfList = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + // assertEquals(receivedlocalIf, initlocalIf); + // assertEquals(receivedlocalIfList, initlocalIfList); + // assertEquals(receivedimportedIf, initimportedIf); + // assertEquals(receivedimportedIfList, initimportedIfList); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, ParentIfServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(ParentIfServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IParentIfEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void onReceivelocalIfPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.PROP_LocalIf.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setLocalIf( any(ISimpleLocalIf.class)); + + } + + @Test + public void whenNotifiedlocalIf() + { + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + + testedAdapterAsEventListener.onLocalIfChanged(testlocalIf); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SET_LocalIf.getValue(), response.what); + Bundle data = response.getData(); + + + ISimpleLocalIf receivedlocalIf = data.getParcelable("localIf", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + + assertEquals(receivedlocalIf, testlocalIf); + } + @Test + public void onReceivelocalIfListPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.PROP_LocalIfList.getValue()); + Bundle data = new Bundle(); + ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setLocalIfList( any(ISimpleLocalIf[].class)); + + } + + @Test + public void whenNotifiedlocalIfList() + { + ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + + testedAdapterAsEventListener.onLocalIfListChanged(testlocalIfList); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SET_LocalIfList.getValue(), response.what); + Bundle data = response.getData(); + + + ISimpleLocalIf[] receivedlocalIfList = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("localIfList", SimpleLocalIfParcelable.class)); + + assertEquals(receivedlocalIfList, testlocalIfList); + } + @Test + public void onReceiveimportedIfPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.PROP_ImportedIf.getValue()); + Bundle data = new Bundle(); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testimportedIf)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setImportedIf( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf.class)); + + } + + @Test + public void whenNotifiedimportedIf() + { + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + + testedAdapterAsEventListener.onImportedIfChanged(testimportedIf); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SET_ImportedIf.getValue(), response.what); + Bundle data = response.getData(); + + + tbIfaceimport.tbIfaceimport_api.IEmptyIf receivedimportedIf = data.getParcelable("importedIf", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + + assertEquals(receivedimportedIf, testimportedIf); + } + @Test + public void onReceiveimportedIfListPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.PROP_ImportedIfList.getValue()); + Bundle data = new Bundle(); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testimportedIfList)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setImportedIfList( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class)); + + } + + @Test + public void whenNotifiedimportedIfList() + { + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + + testedAdapterAsEventListener.onImportedIfListChanged(testimportedIfList); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SET_ImportedIfList.getValue(), response.what); + Bundle data = response.getData(); + + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] receivedimportedIfList = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + + assertEquals(receivedimportedIfList, testimportedIfList); + } + @Test + public void whenNotifiedlocalIfSignal() + { + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + + testedAdapterAsEventListener.onLocalIfSignal(testparam); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SIG_LocalIfSignal.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf receivedparam = data.getParcelable("param", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + assertEquals(receivedparam, testparam); +} + @Test + public void whenNotifiedlocalIfSignalList() + { + ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + + testedAdapterAsEventListener.onLocalIfSignalList(testparam); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SIG_LocalIfSignalList.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + + ISimpleLocalIf[] receivedparam = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])data.getParcelableArray("param", SimpleLocalIfParcelable.class)); + assertEquals(receivedparam, testparam); +} + @Test + public void whenNotifiedimportedIfSignal() + { + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + + testedAdapterAsEventListener.onImportedIfSignal(testparam); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SIG_ImportedIfSignal.getValue(), response.what); + Bundle data = response.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf receivedparam = data.getParcelable("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + assertEquals(receivedparam, testparam); +} + @Test + public void whenNotifiedimportedIfSignalList() + { + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + + testedAdapterAsEventListener.onImportedIfSignalList(testparam); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.SIG_ImportedIfSignalList.getValue(), response.what); + Bundle data = response.getData(); + + // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] receivedparam = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])data.getParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + assertEquals(receivedparam, testparam); +} + + + public void onlocalIfMethodRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.RPC_LocalIfMethodReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + data.putParcelable("param", new SimpleLocalIfParcelable(testparam)); + ISimpleLocalIf returnedValue = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + + + when(backendServiceMock.localIfMethod( any(ISimpleLocalIf.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).localIfMethod( any(ISimpleLocalIf.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.RPC_LocalIfMethodResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + ISimpleLocalIf receivedByClient = resp_data.getParcelable("result", SimpleLocalIfParcelable.class).getSimpleLocalIf(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onlocalIfMethodListRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.RPC_LocalIfMethodListReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(testparam)); + ISimpleLocalIf[] returnedValue = new ISimpleLocalIf[1]; + returnedValue[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + + + when(backendServiceMock.localIfMethodList( any(ISimpleLocalIf[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).localIfMethodList( any(ISimpleLocalIf[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.RPC_LocalIfMethodListResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + ISimpleLocalIf[] receivedByClient = SimpleLocalIfParcelable.unwrapArray((SimpleLocalIfParcelable[])resp_data.getParcelableArray("result", SimpleLocalIfParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onimportedIfMethodRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.RPC_ImportedIfMethodReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + data.putParcelable("param", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testparam)); + tbIfaceimport.tbIfaceimport_api.IEmptyIf returnedValue = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + + + when(backendServiceMock.importedIfMethod( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).importedIfMethod( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.RPC_ImportedIfMethodResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + tbIfaceimport.tbIfaceimport_api.IEmptyIf receivedByClient = resp_data.getParcelable("result", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class).getEmptyIf(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onimportedIfMethodListRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, ParentIfMessageType.RPC_ImportedIfMethodListReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + data.putParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testparam)); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] returnedValue = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + returnedValue[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + + + when(backendServiceMock.importedIfMethodList( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).importedIfMethodList( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(ParentIfMessageType.RPC_ImportedIfMethodListResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] receivedByClient = tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.unwrapArray((tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable[])resp_data.getParcelableArray("result", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java new file mode 100644 index 0000000..3a2db2e --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java @@ -0,0 +1,285 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfaces_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter; + +//import message type and parcelabe types +import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; + + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_android_service.ISimpleLocalIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface ISimpleLocalIfMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class SimpleLocalIfServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private SimpleLocalIfServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private ISimpleLocalIfEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private ISimpleLocalIf backendServiceMock = mock(ISimpleLocalIf.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private ISimpleLocalIfServiceFactory serviceFactory = mock(ISimpleLocalIfServiceFactory.class); + private ISimpleLocalIfMessageGetter clientMessagesStorage = mock(ISimpleLocalIfMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, SimpleLocalIfMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(ISimpleLocalIfMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + int initintProperty = 1; + when(backendServiceMock.getIntProperty()).thenReturn(initintProperty); + + + Message registerMsg = Message.obtain(null, SimpleLocalIfMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getIntProperty(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleLocalIfMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + int receivedintProperty = data.getInt("intProperty", 0); + + assertEquals(receivedintProperty, initintProperty); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, SimpleLocalIfServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(SimpleLocalIfServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(ISimpleLocalIfEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void onReceiveintPropertyPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleLocalIfMessageType.PROP_IntProperty.getValue()); + Bundle data = new Bundle(); + int testintProperty = 1; + data.putInt("intProperty", testintProperty); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setIntProperty(testintProperty); + + } + + @Test + public void whenNotifiedintProperty() + { + int testintProperty = 1; + + testedAdapterAsEventListener.onIntPropertyChanged(testintProperty); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleLocalIfMessageType.SET_IntProperty.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedintProperty = data.getInt("intProperty", 0); + + assertEquals(receivedintProperty, testintProperty); + } + @Test + public void whenNotifiedintSignal() + { + int testparam = 1; + + testedAdapterAsEventListener.onIntSignal(testparam); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleLocalIfMessageType.SIG_IntSignal.getValue(), response.what); + Bundle data = response.getData(); + + + int receivedparam = data.getInt("param", 0); + assertEquals(receivedparam, testparam); +} + + + public void onintMethodRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleLocalIfMessageType.RPC_IntMethodReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + int testparam = 1; + data.putInt("param", testparam); + int returnedValue = 1; + + + when(backendServiceMock.intMethod(testparam)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).intMethod(testparam); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleLocalIfMessageType.RPC_IntMethodResp.getValue(), response.what); + Bundle resp_data = response.getData(); + int receivedByClient = resp_data.getInt("result", 0); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/additions.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_api/additions.gradle new file mode 100644 index 0000000..a8d8b13 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/additions.gradle @@ -0,0 +1,8 @@ +android { + namespace 'tbRefIfaces.tbRefIfaces_api' +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + api project(':tbIfaceimport_api') +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_api/build.gradle new file mode 100644 index 0000000..3e0dd7a --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java-library' +} + +group = "tbRefIfaces" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0' + api 'tbIfaceimport:tbIfaceimport_api:1.0.0' +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractParentIf.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractParentIf.java new file mode 100644 index 0000000..6bece7f --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractParentIf.java @@ -0,0 +1,80 @@ +package tbRefIfaces.tbRefIfaces_api; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.IParentIf; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractParentIf implements IParentIf { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IParentIfEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IParentIfEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireLocalIfChanged(ISimpleLocalIf newValue) { + for (IParentIfEventListener listener : listeners) { + listener.onLocalIfChanged(newValue); + } + } + + @Override + public void fireLocalIfListChanged(ISimpleLocalIf[] newValue) { + for (IParentIfEventListener listener : listeners) { + listener.onLocalIfListChanged(newValue); + } + } + + @Override + public void fireImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue) { + for (IParentIfEventListener listener : listeners) { + listener.onImportedIfChanged(newValue); + } + } + + @Override + public void fireImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue) { + for (IParentIfEventListener listener : listeners) { + listener.onImportedIfListChanged(newValue); + } + } + + @Override + public void fireLocalIfSignal(ISimpleLocalIf param) { + for (IParentIfEventListener listener : listeners) { + listener.onLocalIfSignal(param); + } + } + + @Override + public void fireLocalIfSignalList(ISimpleLocalIf[] param) { + for (IParentIfEventListener listener : listeners) { + listener.onLocalIfSignalList(param); + } + } + + @Override + public void fireImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + for (IParentIfEventListener listener : listeners) { + listener.onImportedIfSignal(param); + } + } + + @Override + public void fireImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + for (IParentIfEventListener listener : listeners) { + listener.onImportedIfSignalList(param); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IParentIfEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractSimpleLocalIf.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractSimpleLocalIf.java new file mode 100644 index 0000000..e480888 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/AbstractSimpleLocalIf.java @@ -0,0 +1,38 @@ +package tbRefIfaces.tbRefIfaces_api; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractSimpleLocalIf implements ISimpleLocalIf { + public Collection listeners = new HashSet<>(); + + public void addEventListener(ISimpleLocalIfEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(ISimpleLocalIfEventListener listener) { + listeners.remove(listener); + } + @Override + public void fireIntPropertyChanged(int newValue) { + for (ISimpleLocalIfEventListener listener : listeners) { + listener.onIntPropertyChanged(newValue); + } + } + + @Override + public void fireIntSignal(int param) { + for (ISimpleLocalIfEventListener listener : listeners) { + listener.onIntSignal(param); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (ISimpleLocalIfEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIf.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIf.java new file mode 100644 index 0000000..c94deab --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIf.java @@ -0,0 +1,44 @@ +package tbRefIfaces.tbRefIfaces_api; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface IParentIf { + // properties + void setLocalIf(ISimpleLocalIf localIf); + ISimpleLocalIf getLocalIf(); + void fireLocalIfChanged(ISimpleLocalIf newValue); + + void setLocalIfList(ISimpleLocalIf[] localIfList); + ISimpleLocalIf[] getLocalIfList(); + void fireLocalIfListChanged(ISimpleLocalIf[] newValue); + + void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf); + tbIfaceimport.tbIfaceimport_api.IEmptyIf getImportedIf(); + void fireImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue); + + void setImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList(); + void fireImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue); + + // methods + ISimpleLocalIf localIfMethod(ISimpleLocalIf param); + CompletableFuture localIfMethodAsync(ISimpleLocalIf param); + ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param); + CompletableFuture localIfMethodListAsync(ISimpleLocalIf[] param); + tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param); + CompletableFuture importedIfMethodAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf param); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param); + CompletableFuture importedIfMethodListAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param); + public void fireLocalIfSignal(ISimpleLocalIf param); + public void fireLocalIfSignalList(ISimpleLocalIf[] param); + public void fireImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param); + public void fireImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IParentIfEventListener listener); + void removeEventListener(IParentIfEventListener listener); + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIfEventListener.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIfEventListener.java new file mode 100644 index 0000000..b3d96ef --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/IParentIfEventListener.java @@ -0,0 +1,13 @@ +package tbRefIfaces.tbRefIfaces_api; + + public interface IParentIfEventListener { + void onLocalIfChanged(ISimpleLocalIf newValue); + void onLocalIfListChanged(ISimpleLocalIf[] newValue); + void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue); + void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue); + void onLocalIfSignal(ISimpleLocalIf param); + void onLocalIfSignalList(ISimpleLocalIf[] param); + void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param); + void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIf.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIf.java new file mode 100644 index 0000000..67f5338 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIf.java @@ -0,0 +1,23 @@ +package tbRefIfaces.tbRefIfaces_api; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; + +import java.util.concurrent.CompletableFuture; + + + public interface ISimpleLocalIf { + // properties + void setIntProperty(int intProperty); + int getIntProperty(); + void fireIntPropertyChanged(int newValue); + + // methods + int intMethod(int param); + CompletableFuture intMethodAsync(int param); + public void fireIntSignal(int param); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(ISimpleLocalIfEventListener listener); + void removeEventListener(ISimpleLocalIfEventListener listener); + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIfEventListener.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIfEventListener.java new file mode 100644 index 0000000..71f836c --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/ISimpleLocalIfEventListener.java @@ -0,0 +1,7 @@ +package tbRefIfaces.tbRefIfaces_api; + + public interface ISimpleLocalIfEventListener { + void onIntPropertyChanged(int newValue); + void onIntSignal(int param); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/TbRefIfacesTestHelper.java b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/TbRefIfacesTestHelper.java new file mode 100644 index 0000000..d5c23eb --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_api/src/main/java/tbRefIfaces/tbRefIfaces_api/TbRefIfacesTestHelper.java @@ -0,0 +1,26 @@ +package tbRefIfaces.tbRefIfaces_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class TbRefIfacesTestHelper +{ + + static public ISimpleLocalIf makeTestSimpleLocalIf(ISimpleLocalIf testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setIntProperty(1); + return testObjToFill; + } + + static public IParentIf makeTestParentIf(IParentIf testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + ISimpleLocalIf[] locallocalIfList = new ISimpleLocalIf[1]; + testObjToFill.setLocalIfList(locallocalIfList); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] localimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + testObjToFill.setImportedIfList(localimportedIfList); + return testObjToFill; + } +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/build.gradle new file mode 100644 index 0000000..0fbd4df --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbRefIfaces" +version = "1.0.0" + +android { + namespace 'tbRefIfaces.tbRefIfaces_client_example' + compileSdk 35 + + defaultConfig { + applicationId "tbRefIfaces.tbRefIfaces_client_example" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbRefIfaces_api') + implementation project(':tbRefIfaces_android_messenger') + implementation project(':tbRefIfaces_android_client') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/AndroidManifest.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/AndroidManifest.xml new file mode 100644 index 0000000..914de54 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java new file mode 100644 index 0000000..29f6045 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java @@ -0,0 +1,227 @@ +package tbRefIfaces.tbRefIfaces_client_example; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbRefIfaces.tbRefIfaces_android_client.SimpleLocalIfClient; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import java.util.concurrent.CompletableFuture; + + + +public class TbRefIfacesTestClientApp extends Activity implements ISimpleLocalIfEventListener +{ + + private static final String TAG = "TbRefIfacesTestClientApp"; + + private SimpleLocalIfClient mClient = null; + + //TODO ALIGN TO YOUR APP + private static String mModuleNameUnreal = "com.example.TestAndroid"; + private static String mModuleNameStub = "tbRefIfaces.tbRefIfacesserviceexample.TbRefIfacesTestServiceApp"; + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bIntProperty = new Button(this); + bIntProperty.setText("Set intProperty"); + bIntProperty.setBackgroundColor(Color.GREEN); + + bIntProperty.setOnClickListener(v -> { + int newIntProperty = mClient.getIntProperty(); + + //TODO increment + Log.i(TAG, "SET intProperty" + newIntProperty); + mClient.setIntProperty(newIntProperty); + }); + propertyButtonsLine.addView(bIntProperty); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bIntMethod = new Button(this); + bIntMethod.setText("intMethod"); + + bIntMethod.setOnClickListener(v -> { + Log.w(TAG, "CALLING METHOD intMethod "); + int param = 1; + CompletableFuture method_res + = mClient.intMethodAsync(param).thenApply( + i -> { + outputTextVieMethodRes.setText("Got intMethod result "+ i); + return i; + }); + }); + bIntMethod.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bIntMethod); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("Bind to Unreal"); + testButton5.setOnClickListener(v -> { + initServiceConnection(mModuleNameUnreal); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("Bind to Stub app"); + testButton6.setOnClickListener(v -> { + initServiceConnection(mModuleNameStub); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + Button testButton4 = new Button(this); + testButton4.setText("Unbind service"); + testButton4.setOnClickListener(v -> { + if (mClient != null) { + mClient.unbindFromService(); + } + }); + testButton4.setBackgroundColor(Color.GREEN); + layout.addView(testButton4); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + + outputTextViewBIND = new TextView(this); + outputTextViewBIND.setText("NOT BINDED"); + outputTextViewBIND.setTextSize(16); + outputTextViewBIND.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewBIND); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + + if (mClient != null && !mClient.isBoundToService()) + { + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "My app: bind " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + //unbindFromService + } + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, unbinding"); + if (mClient != null) { + mClient.unbindFromService(); + } + mClient.removeEventListener(this); + super.onDestroy(); + } + + private void initServiceConnection( String servicePackage) + { + lastServicePackage = servicePackage; + Log.w(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new SimpleLocalIfClient(this.getApplicationContext(), ""); + Log.w(TAG, "client created "); + mClient.addEventListener(this); + } + + boolean res = mClient.bindToService(lastServicePackage); + Log.v(TAG, "Bindding result " + res); + outputTextViewBIND.setText(res+": to "+lastServicePackage); + } + @Override + public void onIntPropertyChanged(int newValue) + { + outputTextViewProp.setText("Property from service: intProperty " + newValue); + Log.w(TAG, "Property from service: intProperty " + newValue); + } + @Override + public void onIntSignal(int param) + { + String text = "Signal intSignal "+ " " + param; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values-night/themes.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/colors.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/strings.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/strings.xml new file mode 100644 index 0000000..f4bc5cb --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbRefIfacesTestClientApp + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/themes.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/backup_rules.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/additions.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/additions.gradle new file mode 100644 index 0000000..ca864e5 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/additions.gradle @@ -0,0 +1,19 @@ + +android { + namespace 'tbRefIfaces.tbRefIfaces_impl' + + tasks.register('packageSources', Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs + include '**/*.java' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/build.gradle new file mode 100644 index 0000000..7f10257 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/build.gradle @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "tbRefIfaces" +version = "1.0.0" + +android { + namespace 'tbRefIfaces.tbRefIfaces_impl' + compileSdk 35 + + defaultConfig { + minSdk 33 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation project(':tbRefIfaces_api') + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java new file mode 100644 index 0000000..5a9d922 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java @@ -0,0 +1,219 @@ +package tbRefIfaces.tbRefIfaces_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class ParentIfService extends AbstractParentIf { + + private final static String TAG = "ParentIfService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private ISimpleLocalIf m_localIf = null; + private ISimpleLocalIf[] m_localIfList = new ISimpleLocalIf[]{}; + private tbIfaceimport.tbIfaceimport_api.IEmptyIf m_importedIf = null; + private tbIfaceimport.tbIfaceimport_api.IEmptyIf[] m_importedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}; + + public ParentIfService() + { + fire_readyStatusChanged(true); + } + @Override + public void setLocalIf(ISimpleLocalIf localIf) + { + Log.i(TAG, "request setLocalIf called "); + if (! m_localIf.equals(localIf)) + { + m_localIf = localIf; + onLocalIfChanged(m_localIf); + } + + } + + @Override + public ISimpleLocalIf getLocalIf() + { + Log.i(TAG, "request getLocalIf called,"); + return m_localIf; + } + + + @Override + public void setLocalIfList(ISimpleLocalIf[] localIfList) + { + Log.i(TAG, "request setLocalIfList called "); + if (! Arrays.equals(m_localIfList, localIfList)) + { + m_localIfList = localIfList; + onLocalIfListChanged(m_localIfList); + } + + } + + @Override + public ISimpleLocalIf[] getLocalIfList() + { + Log.i(TAG, "request getLocalIfList called,"); + return m_localIfList; + } + + + @Override + public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) + { + Log.i(TAG, "request setImportedIf called "); + if (! m_importedIf.equals(importedIf)) + { + m_importedIf = importedIf; + onImportedIfChanged(m_importedIf); + } + + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf getImportedIf() + { + Log.i(TAG, "request getImportedIf called,"); + return m_importedIf; + } + + + @Override + public void setImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList) + { + Log.i(TAG, "request setImportedIfList called "); + if (! Arrays.equals(m_importedIfList, importedIfList)) + { + m_importedIfList = importedIfList; + onImportedIfListChanged(m_importedIfList); + } + + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList() + { + Log.i(TAG, "request getImportedIfList called,"); + return m_importedIfList; + } + + + // methods + + @Override + public ISimpleLocalIf localIfMethod(ISimpleLocalIf param) { + Log.w(TAG, "request method localIfMethod called, returnig default"); + return null; + } + + @Override + public CompletableFuture localIfMethodAsync(ISimpleLocalIf param) { + return CompletableFuture.supplyAsync( + () -> {return localIfMethod(param); }, + executor); + } + + @Override + public ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param) { + Log.w(TAG, "request method localIfMethodList called, returnig default"); + return new ISimpleLocalIf[]{}; + } + + @Override + public CompletableFuture localIfMethodListAsync(ISimpleLocalIf[] param) { + return CompletableFuture.supplyAsync( + () -> {return localIfMethodList(param); }, + executor); + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + Log.w(TAG, "request method importedIfMethod called, returnig default"); + return null; + } + + @Override + public CompletableFuture importedIfMethodAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + return CompletableFuture.supplyAsync( + () -> {return importedIfMethod(param); }, + executor); + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + Log.w(TAG, "request method importedIfMethodList called, returnig default"); + return new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}; + } + + @Override + public CompletableFuture importedIfMethodListAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + return CompletableFuture.supplyAsync( + () -> {return importedIfMethodList(param); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onLocalIfChanged(ISimpleLocalIf newValue) + { + Log.i(TAG, "onLocalIfChanged, will pass notification to all listeners"); + fireLocalIfChanged(newValue); + } + private void onLocalIfListChanged(ISimpleLocalIf[] newValue) + { + Log.i(TAG, "onLocalIfListChanged, will pass notification to all listeners"); + fireLocalIfListChanged(newValue); + } + private void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue) + { + Log.i(TAG, "onImportedIfChanged, will pass notification to all listeners"); + fireImportedIfChanged(newValue); + } + private void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue) + { + Log.i(TAG, "onImportedIfListChanged, will pass notification to all listeners"); + fireImportedIfListChanged(newValue); + } + public void onLocalIfSignal(ISimpleLocalIf param) + { + Log.i(TAG, "onLocalIfSignal, will pass notification to all listeners"); + fireLocalIfSignal(param); + } + public void onLocalIfSignalList(ISimpleLocalIf[] param) + { + Log.i(TAG, "onLocalIfSignalList, will pass notification to all listeners"); + fireLocalIfSignalList(param); + } + public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.i(TAG, "onImportedIfSignal, will pass notification to all listeners"); + fireImportedIfSignal(param); + } + public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.i(TAG, "onImportedIfSignalList, will pass notification to all listeners"); + fireImportedIfSignalList(param); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java new file mode 100644 index 0000000..42252e1 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java @@ -0,0 +1,85 @@ +package tbRefIfaces.tbRefIfaces_impl; + +import android.os.Messenger; +import android.util.Log; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class SimpleLocalIfService extends AbstractSimpleLocalIf { + + private final static String TAG = "SimpleLocalIfService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private int m_intProperty = 0; + + public SimpleLocalIfService() + { + fire_readyStatusChanged(true); + } + @Override + public void setIntProperty(int intProperty) + { + Log.i(TAG, "request setIntProperty called "); + if (m_intProperty != intProperty) + { + m_intProperty = intProperty; + onIntPropertyChanged(m_intProperty); + } + + } + + @Override + public int getIntProperty() + { + Log.i(TAG, "request getIntProperty called,"); + return m_intProperty; + } + + + // methods + + @Override + public int intMethod(int param) { + Log.w(TAG, "request method intMethod called, returnig default"); + return 0; + } + + @Override + public CompletableFuture intMethodAsync(int param) { + return CompletableFuture.supplyAsync( + () -> {return intMethod(param); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onIntPropertyChanged(int newValue) + { + Log.i(TAG, "onIntPropertyChanged, will pass notification to all listeners"); + fireIntPropertyChanged(newValue); + } + public void onIntSignal(int param) + { + Log.i(TAG, "onIntSignal, will pass notification to all listeners"); + fireIntSignal(param); + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java new file mode 100644 index 0000000..9e5da76 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java @@ -0,0 +1,257 @@ +package tbRefIfaces.tbRefIfacesjniclient; + +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; + +import tbRefIfaces.tbRefIfaces_android_client.ParentIfClient; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class ParentIfJniClient extends AbstractParentIf implements IParentIfEventListener +{ + + private static final String TAG = "ParentIfJniClient"; + + private ParentIfClient mMessengerClient = null; + + + private static String ModuleName = "tbRefIfaces.tbRefIfacesjniservice.ParentIfJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setLocalIf(ISimpleLocalIf localIf) + { + Log.i(TAG, "got request from ue, setLocalIf" + (localIf)); + mMessengerClient.setLocalIf(localIf); + } + @Override + public ISimpleLocalIf getLocalIf() + { + Log.i(TAG, "got request from ue, getLocalIf"); + return mMessengerClient.getLocalIf(); + } + + @Override + public void setLocalIfList(ISimpleLocalIf[] localIfList) + { + Log.i(TAG, "got request from ue, setLocalIfList" + (localIfList)); + mMessengerClient.setLocalIfList(localIfList); + } + @Override + public ISimpleLocalIf[] getLocalIfList() + { + Log.i(TAG, "got request from ue, getLocalIfList"); + return mMessengerClient.getLocalIfList(); + } + + @Override + public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) + { + Log.i(TAG, "got request from ue, setImportedIf" + (importedIf)); + mMessengerClient.setImportedIf(importedIf); + } + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf getImportedIf() + { + Log.i(TAG, "got request from ue, getImportedIf"); + return mMessengerClient.getImportedIf(); + } + + @Override + public void setImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList) + { + Log.i(TAG, "got request from ue, setImportedIfList" + (importedIfList)); + mMessengerClient.setImportedIfList(importedIfList); + } + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList() + { + Log.i(TAG, "got request from ue, getImportedIfList"); + return mMessengerClient.getImportedIfList(); + } + + public ISimpleLocalIf localIfMethod(ISimpleLocalIf param) + { + Log.v(TAG, "Blocking calllocalIfMethod - should not be used "); + return mMessengerClient.localIfMethod(param); + } + + public void localIfMethodAsync(String callId, ISimpleLocalIf param){ + Log.v(TAG, "non blocking call localIfMethod "); + mMessengerClient.localIfMethodAsync(param).thenAccept(i -> { + nativeOnLocalIfMethodResult(i, callId);}); + } + + //Should not be called directly, use localIfMethodAsync(String callId, ISimpleLocalIf param) + public CompletableFuture localIfMethodAsync(ISimpleLocalIf param) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.localIfMethodAsync(param); + } + public ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param) + { + Log.v(TAG, "Blocking calllocalIfMethodList - should not be used "); + return mMessengerClient.localIfMethodList(param); + } + + public void localIfMethodListAsync(String callId, ISimpleLocalIf[] param){ + Log.v(TAG, "non blocking call localIfMethodList "); + mMessengerClient.localIfMethodListAsync(param).thenAccept(i -> { + nativeOnLocalIfMethodListResult(i, callId);}); + } + + //Should not be called directly, use localIfMethodListAsync(String callId, ISimpleLocalIf[] param) + public CompletableFuture localIfMethodListAsync(ISimpleLocalIf[] param) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.localIfMethodListAsync(param); + } + public tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.v(TAG, "Blocking callimportedIfMethod - should not be used "); + return mMessengerClient.importedIfMethod(param); + } + + public void importedIfMethodAsync(String callId, tbIfaceimport.tbIfaceimport_api.IEmptyIf param){ + Log.v(TAG, "non blocking call importedIfMethod "); + mMessengerClient.importedIfMethodAsync(param).thenAccept(i -> { + nativeOnImportedIfMethodResult(i, callId);}); + } + + //Should not be called directly, use importedIfMethodAsync(String callId, tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + public CompletableFuture importedIfMethodAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.importedIfMethodAsync(param); + } + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.v(TAG, "Blocking callimportedIfMethodList - should not be used "); + return mMessengerClient.importedIfMethodList(param); + } + + public void importedIfMethodListAsync(String callId, tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param){ + Log.v(TAG, "non blocking call importedIfMethodList "); + mMessengerClient.importedIfMethodListAsync(param).thenAccept(i -> { + nativeOnImportedIfMethodListResult(i, callId);}); + } + + //Should not be called directly, use importedIfMethodListAsync(String callId, tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + public CompletableFuture importedIfMethodListAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.importedIfMethodListAsync(param); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new ParentIfClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onLocalIfChanged(ISimpleLocalIf newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnLocalIfChanged(newValue); + } + @Override + public void onLocalIfListChanged(ISimpleLocalIf[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnLocalIfListChanged(newValue); + } + @Override + public void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnImportedIfChanged(newValue); + } + @Override + public void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnImportedIfListChanged(newValue); + } + @Override + public void onLocalIfSignal(ISimpleLocalIf param) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal localIfSignal "+ " " + param); + nativeOnLocalIfSignal(param); + } + @Override + public void onLocalIfSignalList(ISimpleLocalIf[] param) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal localIfSignalList "+ " " + param); + nativeOnLocalIfSignalList(param); + } + @Override + public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal importedIfSignal "+ " " + param); + nativeOnImportedIfSignal(param); + } + @Override + public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal importedIfSignalList "+ " " + param); + nativeOnImportedIfSignalList(param); + } + private native void nativeOnLocalIfChanged(ISimpleLocalIf localIf); + private native void nativeOnLocalIfListChanged(ISimpleLocalIf[] localIfList); + private native void nativeOnImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf); + private native void nativeOnImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList); + private native void nativeOnLocalIfSignal(ISimpleLocalIf param); + private native void nativeOnLocalIfSignalList(ISimpleLocalIf[] param); + private native void nativeOnImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param); + private native void nativeOnImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param); + private native void nativeOnLocalIfMethodResult(ISimpleLocalIf result, String callId); + private native void nativeOnLocalIfMethodListResult(ISimpleLocalIf[] result, String callId); + private native void nativeOnImportedIfMethodResult(tbIfaceimport.tbIfaceimport_api.IEmptyIf result, String callId); + private native void nativeOnImportedIfMethodListResult(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java new file mode 100644 index 0000000..c33366a --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java @@ -0,0 +1,117 @@ +package tbRefIfaces.tbRefIfacesjniclient; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; + +import tbRefIfaces.tbRefIfaces_android_client.SimpleLocalIfClient; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class SimpleLocalIfJniClient extends AbstractSimpleLocalIf implements ISimpleLocalIfEventListener +{ + + private static final String TAG = "SimpleLocalIfJniClient"; + + private SimpleLocalIfClient mMessengerClient = null; + + + private static String ModuleName = "tbRefIfaces.tbRefIfacesjniservice.SimpleLocalIfJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setIntProperty(int intProperty) + { + Log.i(TAG, "got request from ue, setIntProperty" + (intProperty)); + mMessengerClient.setIntProperty(intProperty); + } + @Override + public int getIntProperty() + { + Log.i(TAG, "got request from ue, getIntProperty"); + return mMessengerClient.getIntProperty(); + } + + public int intMethod(int param) + { + Log.v(TAG, "Blocking callintMethod - should not be used "); + return mMessengerClient.intMethod(param); + } + + public void intMethodAsync(String callId, int param){ + Log.v(TAG, "non blocking call intMethod "); + mMessengerClient.intMethodAsync(param).thenAccept(i -> { + nativeOnIntMethodResult(i, callId);}); + } + + //Should not be called directly, use intMethodAsync(String callId, int param) + public CompletableFuture intMethodAsync(int param) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.intMethodAsync(param); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SimpleLocalIfClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onIntPropertyChanged(int newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnIntPropertyChanged(newValue); + } + @Override + public void onIntSignal(int param) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal intSignal "+ " " + param); + nativeOnIntSignal(param); + } + private native void nativeOnIntPropertyChanged(int intProperty); + private native void nativeOnIntSignal(int param); + private native void nativeOnIntMethodResult(int result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java new file mode 100644 index 0000000..b2b2279 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java @@ -0,0 +1,218 @@ +package tbRefIfaces.tbRefIfacesjniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class ParentIfJniService extends AbstractParentIf { + + + private final static String TAG = "ParentIfJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public ParentIfJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setLocalIf(ISimpleLocalIf localIf) + { + Log.i(TAG, "request setLocalIf called, will call native "); + nativeSetLocalIf(localIf); + } + + @Override + public ISimpleLocalIf getLocalIf() + { + Log.i(TAG, "request getLocalIf called, will call native "); + return nativeGetLocalIf(); + } + + + @Override + public void setLocalIfList(ISimpleLocalIf[] localIfList) + { + Log.i(TAG, "request setLocalIfList called, will call native "); + nativeSetLocalIfList(localIfList); + } + + @Override + public ISimpleLocalIf[] getLocalIfList() + { + Log.i(TAG, "request getLocalIfList called, will call native "); + return nativeGetLocalIfList(); + } + + + @Override + public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) + { + Log.i(TAG, "request setImportedIf called, will call native "); + nativeSetImportedIf(importedIf); + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf getImportedIf() + { + Log.i(TAG, "request getImportedIf called, will call native "); + return nativeGetImportedIf(); + } + + + @Override + public void setImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList) + { + Log.i(TAG, "request setImportedIfList called, will call native "); + nativeSetImportedIfList(importedIfList); + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList() + { + Log.i(TAG, "request getImportedIfList called, will call native "); + return nativeGetImportedIfList(); + } + + + // methods + + @Override + public ISimpleLocalIf localIfMethod(ISimpleLocalIf param) { + Log.w(TAG, "request method localIfMethod called, will call native"); + return nativeLocalIfMethod(param); + } + + @Override + public CompletableFuture localIfMethodAsync(ISimpleLocalIf param) { + return CompletableFuture.supplyAsync( + () -> {return localIfMethod(param); }, + executor); + } + + @Override + public ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param) { + Log.w(TAG, "request method localIfMethodList called, will call native"); + return nativeLocalIfMethodList(param); + } + + @Override + public CompletableFuture localIfMethodListAsync(ISimpleLocalIf[] param) { + return CompletableFuture.supplyAsync( + () -> {return localIfMethodList(param); }, + executor); + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + Log.w(TAG, "request method importedIfMethod called, will call native"); + return nativeImportedIfMethod(param); + } + + @Override + public CompletableFuture importedIfMethodAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { + return CompletableFuture.supplyAsync( + () -> {return importedIfMethod(param); }, + executor); + } + + @Override + public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + Log.w(TAG, "request method importedIfMethodList called, will call native"); + return nativeImportedIfMethodList(param); + } + + @Override + public CompletableFuture importedIfMethodListAsync(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { + return CompletableFuture.supplyAsync( + () -> {return importedIfMethodList(param); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetLocalIf(ISimpleLocalIf localIf); + private native ISimpleLocalIf nativeGetLocalIf(); + + private native void nativeSetLocalIfList(ISimpleLocalIf[] localIfList); + private native ISimpleLocalIf[] nativeGetLocalIfList(); + + private native void nativeSetImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf); + private native tbIfaceimport.tbIfaceimport_api.IEmptyIf nativeGetImportedIf(); + + private native void nativeSetImportedIfList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfList); + private native tbIfaceimport.tbIfaceimport_api.IEmptyIf[] nativeGetImportedIfList(); + + // methods + private native ISimpleLocalIf nativeLocalIfMethod(ISimpleLocalIf param); + private native ISimpleLocalIf[] nativeLocalIfMethodList(ISimpleLocalIf[] param); + private native tbIfaceimport.tbIfaceimport_api.IEmptyIf nativeImportedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param); + private native tbIfaceimport.tbIfaceimport_api.IEmptyIf[] nativeImportedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onLocalIfChanged(ISimpleLocalIf newValue) + { + Log.i(TAG, "onLocalIfChanged, will pass notification to all listeners"); + fireLocalIfChanged(newValue); + } + public void onLocalIfListChanged(ISimpleLocalIf[] newValue) + { + Log.i(TAG, "onLocalIfListChanged, will pass notification to all listeners"); + fireLocalIfListChanged(newValue); + } + public void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue) + { + Log.i(TAG, "onImportedIfChanged, will pass notification to all listeners"); + fireImportedIfChanged(newValue); + } + public void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue) + { + Log.i(TAG, "onImportedIfListChanged, will pass notification to all listeners"); + fireImportedIfListChanged(newValue); + } + public void onLocalIfSignal(ISimpleLocalIf param) + { + Log.i(TAG, "onLocalIfSignal, will pass notification to all listeners"); + fireLocalIfSignal(param); + } + public void onLocalIfSignalList(ISimpleLocalIf[] param) + { + Log.i(TAG, "onLocalIfSignalList, will pass notification to all listeners"); + fireLocalIfSignalList(param); + } + public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.i(TAG, "onImportedIfSignal, will pass notification to all listeners"); + fireImportedIfSignal(param); + } + public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.i(TAG, "onImportedIfSignalList, will pass notification to all listeners"); + fireImportedIfSignalList(param); + } + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java new file mode 100644 index 0000000..92534bf --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfacesjniservice; + +import tbRefIfaces.tbRefIfaces_android_service.IParentIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_api.AbstractParentIf; +import tbRefIfaces.tbRefIfacesjniservice.ParentIfJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton ParentIfJniServiceFactory thread for the system. This is a thread for + * ParentIfJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class ParentIfJniServiceFactory extends HandlerThread implements IParentIfServiceFactory +{ + private ParentIfJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static ParentIfJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: ParentIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractParentIf getServiceInstance() + { + if (jniService == null) + { + jniService = new ParentIfJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new ParentIfJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final ParentIfJniServiceFactory INSTANCE = createInstance(); + } + + private ParentIfJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static ParentIfJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + ParentIfJniServiceFactory t = new ParentIfJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java new file mode 100644 index 0000000..086745c --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbRefIfaces.tbRefIfacesjniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_android_service.ParentIfServiceAdapter; +import tbRefIfaces.tbRefIfacesjniservice.ParentIfJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class ParentIfJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "ParentIfJniStarter"; + + + + public static IParentIf start(Context context) { + stop(context); + androidService = new Intent(context, ParentIfServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + ParentIfJniServiceFactory factory = ParentIfJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for ParentIfJniServiceFactory"); + return ParentIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java new file mode 100644 index 0000000..82a4a0c --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java @@ -0,0 +1,90 @@ +package tbRefIfaces.tbRefIfacesjniservice; + +import android.os.Messenger; +import android.util.Log; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class SimpleLocalIfJniService extends AbstractSimpleLocalIf { + + + private final static String TAG = "SimpleLocalIfJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public SimpleLocalIfJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setIntProperty(int intProperty) + { + Log.i(TAG, "request setIntProperty called, will call native "); + nativeSetIntProperty(intProperty); + } + + @Override + public int getIntProperty() + { + Log.i(TAG, "request getIntProperty called, will call native "); + return nativeGetIntProperty(); + } + + + // methods + + @Override + public int intMethod(int param) { + Log.w(TAG, "request method intMethod called, will call native"); + return nativeIntMethod(param); + } + + @Override + public CompletableFuture intMethodAsync(int param) { + return CompletableFuture.supplyAsync( + () -> {return intMethod(param); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetIntProperty(int intProperty); + private native int nativeGetIntProperty(); + + // methods + private native int nativeIntMethod(int param); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onIntPropertyChanged(int newValue) + { + Log.i(TAG, "onIntPropertyChanged, will pass notification to all listeners"); + fireIntPropertyChanged(newValue); + } + public void onIntSignal(int param) + { + Log.i(TAG, "onIntSignal, will pass notification to all listeners"); + fireIntSignal(param); + } + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java new file mode 100644 index 0000000..2813b8d --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package tbRefIfaces.tbRefIfacesjniservice; + +import tbRefIfaces.tbRefIfaces_android_service.ISimpleLocalIfServiceFactory; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_api.AbstractSimpleLocalIf; +import tbRefIfaces.tbRefIfacesjniservice.SimpleLocalIfJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton SimpleLocalIfJniServiceFactory thread for the system. This is a thread for + * SimpleLocalIfJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class SimpleLocalIfJniServiceFactory extends HandlerThread implements ISimpleLocalIfServiceFactory +{ + private SimpleLocalIfJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static SimpleLocalIfJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: SimpleLocalIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractSimpleLocalIf getServiceInstance() + { + if (jniService == null) + { + jniService = new SimpleLocalIfJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new SimpleLocalIfJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final SimpleLocalIfJniServiceFactory INSTANCE = createInstance(); + } + + private SimpleLocalIfJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static SimpleLocalIfJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + SimpleLocalIfJniServiceFactory t = new SimpleLocalIfJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java new file mode 100644 index 0000000..f38c23a --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java @@ -0,0 +1,42 @@ +package tbRefIfaces.tbRefIfacesjniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter; +import tbRefIfaces.tbRefIfacesjniservice.SimpleLocalIfJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class SimpleLocalIfJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "SimpleLocalIfJniStarter"; + + + + public static ISimpleLocalIf start(Context context) { + stop(context); + androidService = new Intent(context, SimpleLocalIfServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + SimpleLocalIfJniServiceFactory factory = SimpleLocalIfJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for SimpleLocalIfJniServiceFactory"); + return SimpleLocalIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/build.gradle new file mode 100644 index 0000000..2e75d6b --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbRefIfaces" +version = "1.0.0" + + +android { + namespace 'tbRefIfaces.tbRefIfacesserviceexample' + compileSdk 35 + + defaultConfig { + applicationId "tbRefIfaces.tbRefIfacesserviceexample" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation project(':tbRefIfaces_api') + implementation project(':tbRefIfaces_android_messenger') + implementation project(':tbRefIfaces_android_service') + implementation project(':tbRefIfaces_impl') + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/AndroidManifest.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..751b770 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java new file mode 100644 index 0000000..e235230 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java @@ -0,0 +1,202 @@ +package tbRefIfaces.tbRefIfacesserviceexample; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.content.Intent; + + +//TODO for each interface there coudl be a tab? now only first one is added + +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceAdapter; +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceFactory; +import tbRefIfaces.tbRefIfaces_android_service.SimpleLocalIfServiceStarter; + +//import message type and parcelabe types + +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; +import java.util.concurrent.CompletableFuture; + + + +public class TbRefIfacesTestServiceApp extends Activity implements ISimpleLocalIfEventListener +{ + + private static final String TAG = "TbRefIfacesTestServiceApp"; + static Intent stub_service = null; + + + private ISimpleLocalIf mBackend = null; + + private TextView outputTextViewProp; + private TextView outputTextViewSig; + private TextView outputTextVieMethodRes; + private TextView outputTextViewBIND; + private String lastServicePackage =""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + View spacer = new View(this); + LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 200 // height in pixels, adjust as needed + ); + layout.addView(spacer, spacerParams); + + layout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + )); + + layout.setBackgroundColor(Color.YELLOW); + + LinearLayout propertyButtonsLine = new LinearLayout(this); + propertyButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bIntProperty = new Button(this); + bIntProperty.setText("Set intProperty"); + bIntProperty.setBackgroundColor(Color.GREEN); + + bIntProperty.setOnClickListener(v -> { + int newIntProperty = mBackend.getIntProperty(); + //TODO increment + Log.i(TAG, "SET intProperty" + newIntProperty); + mBackend.setIntProperty(newIntProperty); + }); + propertyButtonsLine.addView(bIntProperty); + + layout.addView(propertyButtonsLine); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bIntSignal = new Button(this); + bIntSignal.setText("intSignal"); + + bIntSignal.setOnClickListener(v -> { + Log.w(TAG, "broadcasting singal intSignal "); + int param = 1; + mBackend.fireIntSignal(param); + }); + bIntSignal.setBackgroundColor(Color.GREEN); + methodButtonsLine.addView(bIntSignal); + + layout.addView(methodButtonsLine); + + Button testButton5 = new Button(this); + testButton5.setText("start service"); + testButton5.setOnClickListener(v -> { + startMyService(); + }); + testButton5.setBackgroundColor(Color.GREEN); + layout.addView(testButton5); + + Button testButton6 = new Button(this); + testButton6.setText("stop service"); + testButton6.setOnClickListener(v -> { + stopMyService(); + }); + testButton6.setBackgroundColor(Color.GREEN); + layout.addView(testButton6); + + outputTextViewProp = new TextView(this); + outputTextViewProp.setText("Property received from service will appear here"); + outputTextViewProp.setTextSize(16); + outputTextViewProp.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewProp); + + + outputTextViewSig = new TextView(this); + outputTextViewSig.setText("Singals will apper here"); + outputTextViewSig.setTextSize(16); + outputTextViewSig.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextViewSig); + + + outputTextVieMethodRes = new TextView(this); + outputTextVieMethodRes.setText("Method result will appear here"); + outputTextVieMethodRes.setTextSize(16); + outputTextVieMethodRes.setPadding(16, 16, 16, 16); // optional for spacing + layout.addView(outputTextVieMethodRes); + + setContentView(layout); + } + + @Override + protected void onStart() + { + Log.v(TAG, "My app: onStart, binding if not bound"); + super.onStart(); + } + + @Override + protected void onStop() + { + Log.v(TAG, "My app: onStop, NOT UNBINDING"); + super.onStop(); + } + + private void startMyService(){ + stub_service = new Intent(this, SimpleLocalIfServiceAdapter.class); + this.startService(stub_service); + Log.w(TAG, "Service started with stub backend"); + mBackend = SimpleLocalIfServiceAdapter.setService(SimpleLocalIfServiceFactory.get()); + mBackend.addEventListener(this); + } + + public void stopMyService() { + mBackend.removeEventListener(this); + if (stub_service!= null) + { + this.stopService(stub_service); + } + stub_service = null; + mBackend = null; + } + + @Override + protected void onDestroy() + { + Log.v(TAG, "My app: onDestroy, stop service"); + stopMyService(); + super.onDestroy(); + } + @Override + public void onIntPropertyChanged(int newValue) + { + outputTextViewProp.setText("Property from service: intProperty " + newValue); + Log.w(TAG, "Property from service: intProperty " + newValue); + } + @Override + public void onIntSignal(int param) + { + String text = "Signal intSignal "+ " " + param; + outputTextViewSig.setText(text); + Log.w(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.w(TAG, "Connected to service "); + } + else + { + Log.w(TAG, "Disconnected from service "); + } + } + + +} diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_background.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values-night/themes.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..54202f5 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/colors.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/strings.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/strings.xml new file mode 100644 index 0000000..f8eded2 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TbRefIfacesTestServiceApp + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/themes.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/themes.xml new file mode 100644 index 0000000..503477b --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/backup_rules.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/data_extraction_rules.xml b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle b/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle index b5d26f0..5fb3801 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle +++ b/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbSame1_api') diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java index f4473e1..7c9895b 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java @@ -17,14 +17,8 @@ import android.util.Log; //import message type and parcelabe types -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; import tbSame1.tbSame1_api.Enum1; import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; import tbSame1.tbSame1_api.ISameEnum1Interface; @@ -188,7 +182,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); @@ -213,7 +207,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); onSig1(param1); @@ -222,7 +217,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java index 670393b..42e904f 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java @@ -17,10 +17,6 @@ import android.util.Log; //import message type and parcelabe types -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; import tbSame1.tbSame1_api.Enum1; import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; @@ -189,8 +185,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); @@ -229,7 +224,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); onSig1(param1); @@ -238,8 +234,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); @@ -250,7 +246,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -268,8 +265,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java index 7d2cb1d..34943e3 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java @@ -19,12 +19,6 @@ //import message type and parcelabe types import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct1Interface; @@ -188,7 +182,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); @@ -213,7 +207,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); onSig1(param1); @@ -222,7 +217,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java index 6b0b66c..23c8cd5 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java @@ -21,10 +21,6 @@ import tbSame1.tbSame1_android_messenger.Struct1Parcelable; import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_android_messenger.Struct2Parcelable; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct2Interface; @@ -189,8 +185,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); @@ -229,7 +224,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); onSig1(param1); @@ -238,8 +234,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); @@ -250,7 +246,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -268,8 +265,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java index 3ee664a..d98d7cb 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java @@ -153,7 +153,6 @@ public void onInitReceive() throws RemoteException Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -167,7 +166,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } - + @Test public void setPropertyRequestprop1() { @@ -175,7 +174,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -186,6 +184,7 @@ public void setPropertyRequestprop1() Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedprop1, testprop1); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -226,7 +225,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameEnum1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java index ca8fcf2..aa868ee 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java @@ -156,7 +156,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -170,7 +169,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } - + @Test public void setPropertyRequestprop1() { @@ -178,7 +177,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -189,7 +187,7 @@ public void setPropertyRequestprop1() Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -203,7 +201,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); } - + @Test public void setPropertyRequestprop2() { @@ -211,7 +209,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -222,6 +219,7 @@ public void setPropertyRequestprop2() Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); assertEquals(receivedprop2, testprop2); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -280,7 +278,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameEnum2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); @@ -326,8 +325,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameEnum2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java index dbc010e..f138a5d 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java @@ -153,7 +153,6 @@ public void onInitReceive() throws RemoteException Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -167,7 +166,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); } - + @Test public void setPropertyRequestprop1() { @@ -175,7 +174,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -186,6 +184,7 @@ public void setPropertyRequestprop1() Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedprop1, testprop1); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -226,7 +225,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameStruct1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java index d8735f1..78b7a43 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java @@ -156,7 +156,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -170,7 +169,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); } - + @Test public void setPropertyRequestprop1() { @@ -178,7 +177,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -189,7 +187,7 @@ public void setPropertyRequestprop1() Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -203,7 +201,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); } - + @Test public void setPropertyRequestprop2() { @@ -211,7 +209,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -222,6 +219,7 @@ public void setPropertyRequestprop2() Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); assertEquals(receivedprop2, testprop2); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -280,7 +278,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameStruct2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); @@ -326,8 +325,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameStruct2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceParcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceParcelable.java new file mode 100644 index 0000000..2cfad55 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum1InterfaceParcelable.java @@ -0,0 +1,66 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.ISameEnum1Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame1.tbSame1_api.Enum1; + + public class SameEnum1InterfaceParcelable implements Parcelable { + + public ISameEnum1Interface data; + + public SameEnum1InterfaceParcelable(ISameEnum1Interface data) { + this.data = data; + } + + public ISameEnum1Interface getSameEnum1Interface() + { + return data; + } + + protected SameEnum1InterfaceParcelable(Parcel in) { + Enum1Parcelable l_parcelableprop1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameEnum1InterfaceParcelable createFromParcel(Parcel in) { + return new SameEnum1InterfaceParcelable(in); + } + + @Override + public SameEnum1InterfaceParcelable[] newArray(int size) { + return new SameEnum1InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum1Parcelable(data.getProp1()), flags); + + + } + public static SameEnum1InterfaceParcelable[] wrapArray(ISameEnum1Interface[] elements) { + if (elements == null) return null; + SameEnum1InterfaceParcelable[] out = new SameEnum1InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameEnum1InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameEnum1Interface[] unwrapArray(SameEnum1InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameEnum1Interface[] out = new ISameEnum1Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameEnum1Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceParcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceParcelable.java new file mode 100644 index 0000000..96a8bdc --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameEnum2InterfaceParcelable.java @@ -0,0 +1,70 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.ISameEnum2Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_api.Enum2; + + public class SameEnum2InterfaceParcelable implements Parcelable { + + public ISameEnum2Interface data; + + public SameEnum2InterfaceParcelable(ISameEnum2Interface data) { + this.data = data; + } + + public ISameEnum2Interface getSameEnum2Interface() + { + return data; + } + + protected SameEnum2InterfaceParcelable(Parcel in) { + Enum1Parcelable l_parcelableprop1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + Enum2Parcelable l_parcelableprop2 = in.readParcelable(Enum2Parcelable.class.getClassLoader(), Enum2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameEnum2InterfaceParcelable createFromParcel(Parcel in) { + return new SameEnum2InterfaceParcelable(in); + } + + @Override + public SameEnum2InterfaceParcelable[] newArray(int size) { + return new SameEnum2InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum1Parcelable(data.getProp1()), flags); + dest.writeParcelable(new Enum2Parcelable(data.getProp2()), flags); + + + } + public static SameEnum2InterfaceParcelable[] wrapArray(ISameEnum2Interface[] elements) { + if (elements == null) return null; + SameEnum2InterfaceParcelable[] out = new SameEnum2InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameEnum2InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameEnum2Interface[] unwrapArray(SameEnum2InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameEnum2Interface[] out = new ISameEnum2Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameEnum2Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceParcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceParcelable.java new file mode 100644 index 0000000..9a3964a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct1InterfaceParcelable.java @@ -0,0 +1,66 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.ISameStruct1Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame1.tbSame1_api.Struct1; + + public class SameStruct1InterfaceParcelable implements Parcelable { + + public ISameStruct1Interface data; + + public SameStruct1InterfaceParcelable(ISameStruct1Interface data) { + this.data = data; + } + + public ISameStruct1Interface getSameStruct1Interface() + { + return data; + } + + protected SameStruct1InterfaceParcelable(Parcel in) { + Struct1Parcelable l_parcelableprop1 = in.readParcelable(Struct1Parcelable.class.getClassLoader(), Struct1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameStruct1InterfaceParcelable createFromParcel(Parcel in) { + return new SameStruct1InterfaceParcelable(in); + } + + @Override + public SameStruct1InterfaceParcelable[] newArray(int size) { + return new SameStruct1InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Struct1Parcelable(data.getProp1()), flags); + + + } + public static SameStruct1InterfaceParcelable[] wrapArray(ISameStruct1Interface[] elements) { + if (elements == null) return null; + SameStruct1InterfaceParcelable[] out = new SameStruct1InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameStruct1InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameStruct1Interface[] unwrapArray(SameStruct1InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameStruct1Interface[] out = new ISameStruct1Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameStruct1Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceParcelable.java b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceParcelable.java new file mode 100644 index 0000000..1cab3dd --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_messenger/src/main/java/tbSame1/tbSame1_android_messenger/SameStruct2InterfaceParcelable.java @@ -0,0 +1,70 @@ +package tbSame1.tbSame1_android_messenger; + +import tbSame1.tbSame1_api.ISameStruct2Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_api.Struct2; + + public class SameStruct2InterfaceParcelable implements Parcelable { + + public ISameStruct2Interface data; + + public SameStruct2InterfaceParcelable(ISameStruct2Interface data) { + this.data = data; + } + + public ISameStruct2Interface getSameStruct2Interface() + { + return data; + } + + protected SameStruct2InterfaceParcelable(Parcel in) { + Struct2Parcelable l_parcelableprop1 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + Struct2Parcelable l_parcelableprop2 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameStruct2InterfaceParcelable createFromParcel(Parcel in) { + return new SameStruct2InterfaceParcelable(in); + } + + @Override + public SameStruct2InterfaceParcelable[] newArray(int size) { + return new SameStruct2InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Struct2Parcelable(data.getProp1()), flags); + dest.writeParcelable(new Struct2Parcelable(data.getProp2()), flags); + + + } + public static SameStruct2InterfaceParcelable[] wrapArray(ISameStruct2Interface[] elements) { + if (elements == null) return null; + SameStruct2InterfaceParcelable[] out = new SameStruct2InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameStruct2InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameStruct2Interface[] unwrapArray(SameStruct2InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameStruct2Interface[] out = new ISameStruct2Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameStruct2Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle b/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle index afb2d60..ca48eda 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle +++ b/goldenmaster/tbSame1/tbSame1_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbSame1_api') - implementation project(':tbSame1_impl') - implementation project(':tbSame1_android_messenger') + api project(':tbSame1_impl') + api project(':tbSame1_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/tbSame1/tbSame1_android_service/build.gradle b/goldenmaster/tbSame1/tbSame1_android_service/build.gradle index 6c902af..245ec8f 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/build.gradle +++ b/goldenmaster/tbSame1/tbSame1_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "tbSame1" version = "1.0.0" - android { namespace 'tbSame1.tbSame1_android_service' compileSdk 35 diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java index 220789a..0eefc97 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java @@ -14,14 +14,8 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; import tbSame1.tbSame1_api.Enum1; import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; import tbSame1.tbSame1_android_service.ISameEnum1InterfaceServiceFactory; @@ -37,10 +31,11 @@ public class SameEnum1InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameEnum1Interface mBackendService; private static ISameEnum1InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +50,19 @@ public static ISameEnum1Interface setService(ISameEnum1InterfaceServiceFactory f { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameEnum1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +73,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameEnum1InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +108,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -200,7 +219,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java index 62c7e10..df1c137 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java @@ -14,10 +14,6 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; import tbSame1.tbSame1_api.Enum1; import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; @@ -37,10 +33,11 @@ public class SameEnum2InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameEnum2Interface mBackendService; private static ISameEnum2InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +52,19 @@ public static ISameEnum2Interface setService(ISameEnum2InterfaceServiceFactory f { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameEnum2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +75,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameEnum2InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +110,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -209,7 +230,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); @@ -238,8 +260,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java index cb0ab1a..aadebe5 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java @@ -16,12 +16,6 @@ import android.util.Log; import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_android_service.ISameStruct1InterfaceServiceFactory; @@ -37,10 +31,11 @@ public class SameStruct1InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameStruct1Interface mBackendService; private static ISameStruct1InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +50,19 @@ public static ISameStruct1Interface setService(ISameStruct1InterfaceServiceFacto { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameStruct1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +73,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameStruct1InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +108,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -200,7 +219,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java index 44395f2..63454c3 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java @@ -18,10 +18,6 @@ import tbSame1.tbSame1_android_messenger.Struct1Parcelable; import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_android_messenger.Struct2Parcelable; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; import tbSame1.tbSame1_android_service.ISameStruct2InterfaceServiceFactory; @@ -37,10 +33,11 @@ public class SameStruct2InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameStruct2Interface mBackendService; private static ISameStruct2InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +52,19 @@ public static ISameStruct2Interface setService(ISameStruct2InterfaceServiceFacto { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameStruct2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +75,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameStruct2InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +110,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -209,7 +230,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); @@ -238,8 +260,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java index 5e916eb..4f1a7f1 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -165,7 +165,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Bundle data = response.getData(); Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); assertEquals(receivedprop1, initprop1); } @@ -204,7 +205,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -252,7 +252,8 @@ public void whenNotifiedsig1() assertEquals(SameEnum1InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java index aad67b0..3100f3b 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -170,8 +170,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); assertEquals(receivedprop1, initprop1); assertEquals(receivedprop2, initprop2); @@ -211,7 +211,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -246,7 +245,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -294,7 +292,8 @@ public void whenNotifiedsig1() assertEquals(SameEnum2InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); @@ -313,8 +312,8 @@ public void whenNotifiedsig2() assertEquals(SameEnum2InterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java index 79eb221..37f6baa 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -166,7 +166,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Bundle data = response.getData(); Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); } @@ -205,7 +206,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -253,7 +253,8 @@ public void whenNotifiedsig1() assertEquals(SameStruct1InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java index bbea9d6..3ccb9f3 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -172,8 +172,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); // assertEquals(receivedprop2, initprop2); @@ -213,7 +213,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -248,7 +247,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -296,7 +294,8 @@ public void whenNotifiedsig1() assertEquals(SameStruct2InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); @@ -315,8 +314,8 @@ public void whenNotifiedsig2() assertEquals(SameStruct2InterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java index 1d45ded..87ef7a1 100644 --- a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java @@ -2,7 +2,6 @@ import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; import tbSame1.tbSame1_api.ISameEnum1Interface; -//TODO imported/extern modules import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java index 7a45fcd..53077a1 100644 --- a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java @@ -2,7 +2,6 @@ import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; import tbSame1.tbSame1_api.ISameEnum2Interface; -//TODO imported/extern modules import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java index 9991769..e8bb2bd 100644 --- a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java @@ -2,7 +2,6 @@ import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct1Interface; -//TODO imported/extern modules import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java index 9e6266a..e905af3 100644 --- a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java @@ -2,7 +2,6 @@ import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct2Interface; -//TODO imported/extern modules import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; diff --git a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java index 50f5ad9..efbd1db 100644 --- a/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Arrays; - public class TbSame1TestHelper { @@ -26,4 +25,36 @@ static public Struct2 makeTestStruct2() return testStruct; } + static public ISameStruct1Interface makeTestSameStruct1Interface(ISameStruct1Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + Struct1 localprop1 = TbSame1TestHelper.makeTestStruct1(); + testObjToFill.setProp1(localprop1); + return testObjToFill; + } + + static public ISameStruct2Interface makeTestSameStruct2Interface(ISameStruct2Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + Struct2 localprop1 = TbSame1TestHelper.makeTestStruct2(); + testObjToFill.setProp1(localprop1); + Struct2 localprop2 = TbSame1TestHelper.makeTestStruct2(); + testObjToFill.setProp2(localprop2); + return testObjToFill; + } + + static public ISameEnum1Interface makeTestSameEnum1Interface(ISameEnum1Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setProp1(Enum1.Value2); + return testObjToFill; + } + + static public ISameEnum2Interface makeTestSameEnum2Interface(ISameEnum2Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setProp1(Enum1.Value2); + testObjToFill.setProp2(Enum2.Value2); + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_client_example/build.gradle b/goldenmaster/tbSame1/tbSame1_client_example/build.gradle index 7e359c1..683aa3b 100644 --- a/goldenmaster/tbSame1/tbSame1_client_example/build.gradle +++ b/goldenmaster/tbSame1/tbSame1_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "tbSame1" +version = "1.0.0" + android { namespace 'tbSame1.tbSame1_client_example' compileSdk 35 diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java index 28a5d61..ddfbf8a 100644 --- a/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java @@ -13,17 +13,8 @@ //TODO for each interface there coudl be a tab? now only first one is added import tbSame1.tbSame1_android_client.SameStruct1InterfaceClient; - -//import message type and parcelabe types import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; - import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import java.util.concurrent.CompletableFuture; @@ -234,4 +225,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/tbSame1/tbSame1_impl/additions.gradle b/goldenmaster/tbSame1/tbSame1_impl/additions.gradle index bf24bc2..2b12711 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/additions.gradle +++ b/goldenmaster/tbSame1/tbSame1_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'tbSame1.tbSame1_impl' diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java index e422263..6246973 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java @@ -6,10 +6,7 @@ import tbSame1.tbSame1_api.ISameEnum1Interface; import tbSame1.tbSame1_api.AbstractSameEnum1Interface; import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; import java.util.Map; diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java index 4046e67..e2333e7 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java @@ -6,8 +6,6 @@ import tbSame1.tbSame1_api.ISameEnum2Interface; import tbSame1.tbSame1_api.AbstractSameEnum2Interface; import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; import tbSame1.tbSame1_api.Enum2; diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java index 57b4c15..587e620 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java @@ -7,9 +7,6 @@ import tbSame1.tbSame1_api.AbstractSameStruct1Interface; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; import java.util.Map; diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java index bc49388..bb6cae8 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java @@ -8,8 +8,6 @@ import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; import java.util.Map; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java index e2df979..1bc8634 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java @@ -5,10 +5,8 @@ import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; import tbSame1.tbSame1_android_client.SameEnum1InterfaceClient; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java index 34999b4..2c1c270 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java @@ -5,10 +5,10 @@ import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; import tbSame1.tbSame1_android_client.SameEnum2InterfaceClient; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java index 3e7bc7b..1d927af 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java @@ -6,9 +6,7 @@ import tbSame1.tbSame1_android_client.SameStruct1InterfaceClient; import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java index b060b41..bbebab3 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java @@ -6,9 +6,9 @@ import tbSame1.tbSame1_android_client.SameStruct2InterfaceClient; import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java index 14eb8eb..508d226 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java @@ -6,11 +6,8 @@ import tbSame1.tbSame1_api.ISameEnum1Interface; import tbSame1.tbSame1_api.AbstractSameEnum1Interface; import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; - +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java index aef58eb..f736fc0 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java @@ -6,11 +6,10 @@ import tbSame1.tbSame1_api.ISameEnum2Interface; import tbSame1.tbSame1_api.AbstractSameEnum2Interface; import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; -import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; import tbSame1.tbSame1_api.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; - +import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java index 9d70f38..d4186b7 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java @@ -7,10 +7,7 @@ import tbSame1.tbSame1_api.AbstractSameStruct1Interface; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_api.Struct1; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; - +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java index cfb1911..845f59c 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java @@ -7,10 +7,9 @@ import tbSame1.tbSame1_api.AbstractSameStruct2Interface; import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; import tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_api.Enum2; - +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle b/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle index be898ee..6d8bc4e 100644 --- a/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle +++ b/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "tbSame1" +version = "1.0.0" + + android { namespace 'tbSame1.tbSame1serviceexample' compileSdk 35 diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java index d65ee17..356ac01 100644 --- a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java @@ -20,12 +20,6 @@ //import message type and parcelabe types import tbSame1.tbSame1_api.Struct1; import tbSame1.tbSame1_android_messenger.Struct1Parcelable; -import tbSame1.tbSame1_api.Struct2; -import tbSame1.tbSame1_android_messenger.Struct2Parcelable; -import tbSame1.tbSame1_api.Enum1; -import tbSame1.tbSame1_android_messenger.Enum1Parcelable; -import tbSame1.tbSame1_api.Enum2; -import tbSame1.tbSame1_android_messenger.Enum2Parcelable; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct1Interface; diff --git a/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle b/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle index 7df64c3..91ff31b 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle +++ b/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbSame2_api') diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java index 271d8b4..7cfc11d 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java @@ -17,14 +17,8 @@ import android.util.Log; //import message type and parcelabe types -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; import tbSame2.tbSame2_api.Enum1; import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; import tbSame2.tbSame2_api.ISameEnum1Interface; @@ -188,7 +182,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); @@ -213,7 +207,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); onSig1(param1); @@ -222,7 +217,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java index 728942a..fb6ec38 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java @@ -17,10 +17,6 @@ import android.util.Log; //import message type and parcelabe types -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; import tbSame2.tbSame2_api.Enum1; import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; @@ -189,8 +185,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 prop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); @@ -229,7 +224,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); onSig1(param1); @@ -238,8 +234,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); @@ -250,7 +246,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -268,8 +265,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java index 2e23c83..e878cde 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java @@ -19,12 +19,6 @@ //import message type and parcelabe types import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct1Interface; @@ -188,7 +182,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 prop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); @@ -213,7 +207,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); onSig1(param1); @@ -222,7 +217,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java index 338193b..d9b2a01 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java @@ -21,10 +21,6 @@ import tbSame2.tbSame2_android_messenger.Struct1Parcelable; import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_android_messenger.Struct2Parcelable; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct2Interface; @@ -189,8 +185,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); Struct2 prop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); @@ -229,7 +224,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); onSig1(param1); @@ -238,8 +234,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); @@ -250,7 +246,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -268,8 +265,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java index f600e1d..6bc91b1 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java @@ -153,7 +153,6 @@ public void onInitReceive() throws RemoteException Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -167,7 +166,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } - + @Test public void setPropertyRequestprop1() { @@ -175,7 +174,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -186,6 +184,7 @@ public void setPropertyRequestprop1() Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedprop1, testprop1); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -226,7 +225,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameEnum1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java index d3f94b0..e14f559 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java @@ -156,7 +156,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -170,7 +169,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } - + @Test public void setPropertyRequestprop1() { @@ -178,7 +177,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -189,7 +187,7 @@ public void setPropertyRequestprop1() Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -203,7 +201,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); } - + @Test public void setPropertyRequestprop2() { @@ -211,7 +209,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -222,6 +219,7 @@ public void setPropertyRequestprop2() Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); assertEquals(receivedprop2, testprop2); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -280,7 +278,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameEnum2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); @@ -326,8 +325,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameEnum2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java index ac02326..3c96666 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java @@ -153,7 +153,6 @@ public void onInitReceive() throws RemoteException Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -167,7 +166,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct1.class)); } - + @Test public void setPropertyRequestprop1() { @@ -175,7 +174,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -186,6 +184,7 @@ public void setPropertyRequestprop1() Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedprop1, testprop1); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -226,7 +225,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameStruct1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java index f2705d3..a7adec9 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java @@ -156,7 +156,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -170,7 +169,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(Struct2.class)); } - + @Test public void setPropertyRequestprop1() { @@ -178,7 +177,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -189,7 +187,7 @@ public void setPropertyRequestprop1() Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -203,7 +201,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(Struct2.class)); } - + @Test public void setPropertyRequestprop2() { @@ -211,7 +209,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -222,6 +219,7 @@ public void setPropertyRequestprop2() Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); assertEquals(receivedprop2, testprop2); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -280,7 +278,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameStruct2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); @@ -326,8 +325,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(SameStruct2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceParcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceParcelable.java new file mode 100644 index 0000000..fd05afe --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum1InterfaceParcelable.java @@ -0,0 +1,66 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.ISameEnum1Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame2.tbSame2_api.Enum1; + + public class SameEnum1InterfaceParcelable implements Parcelable { + + public ISameEnum1Interface data; + + public SameEnum1InterfaceParcelable(ISameEnum1Interface data) { + this.data = data; + } + + public ISameEnum1Interface getSameEnum1Interface() + { + return data; + } + + protected SameEnum1InterfaceParcelable(Parcel in) { + Enum1Parcelable l_parcelableprop1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameEnum1InterfaceParcelable createFromParcel(Parcel in) { + return new SameEnum1InterfaceParcelable(in); + } + + @Override + public SameEnum1InterfaceParcelable[] newArray(int size) { + return new SameEnum1InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum1Parcelable(data.getProp1()), flags); + + + } + public static SameEnum1InterfaceParcelable[] wrapArray(ISameEnum1Interface[] elements) { + if (elements == null) return null; + SameEnum1InterfaceParcelable[] out = new SameEnum1InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameEnum1InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameEnum1Interface[] unwrapArray(SameEnum1InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameEnum1Interface[] out = new ISameEnum1Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameEnum1Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceParcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceParcelable.java new file mode 100644 index 0000000..b4f85a7 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameEnum2InterfaceParcelable.java @@ -0,0 +1,70 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.ISameEnum2Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_api.Enum2; + + public class SameEnum2InterfaceParcelable implements Parcelable { + + public ISameEnum2Interface data; + + public SameEnum2InterfaceParcelable(ISameEnum2Interface data) { + this.data = data; + } + + public ISameEnum2Interface getSameEnum2Interface() + { + return data; + } + + protected SameEnum2InterfaceParcelable(Parcel in) { + Enum1Parcelable l_parcelableprop1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + Enum2Parcelable l_parcelableprop2 = in.readParcelable(Enum2Parcelable.class.getClassLoader(), Enum2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameEnum2InterfaceParcelable createFromParcel(Parcel in) { + return new SameEnum2InterfaceParcelable(in); + } + + @Override + public SameEnum2InterfaceParcelable[] newArray(int size) { + return new SameEnum2InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum1Parcelable(data.getProp1()), flags); + dest.writeParcelable(new Enum2Parcelable(data.getProp2()), flags); + + + } + public static SameEnum2InterfaceParcelable[] wrapArray(ISameEnum2Interface[] elements) { + if (elements == null) return null; + SameEnum2InterfaceParcelable[] out = new SameEnum2InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameEnum2InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameEnum2Interface[] unwrapArray(SameEnum2InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameEnum2Interface[] out = new ISameEnum2Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameEnum2Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceParcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceParcelable.java new file mode 100644 index 0000000..d8093f8 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct1InterfaceParcelable.java @@ -0,0 +1,66 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.ISameStruct1Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame2.tbSame2_api.Struct1; + + public class SameStruct1InterfaceParcelable implements Parcelable { + + public ISameStruct1Interface data; + + public SameStruct1InterfaceParcelable(ISameStruct1Interface data) { + this.data = data; + } + + public ISameStruct1Interface getSameStruct1Interface() + { + return data; + } + + protected SameStruct1InterfaceParcelable(Parcel in) { + Struct1Parcelable l_parcelableprop1 = in.readParcelable(Struct1Parcelable.class.getClassLoader(), Struct1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameStruct1InterfaceParcelable createFromParcel(Parcel in) { + return new SameStruct1InterfaceParcelable(in); + } + + @Override + public SameStruct1InterfaceParcelable[] newArray(int size) { + return new SameStruct1InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Struct1Parcelable(data.getProp1()), flags); + + + } + public static SameStruct1InterfaceParcelable[] wrapArray(ISameStruct1Interface[] elements) { + if (elements == null) return null; + SameStruct1InterfaceParcelable[] out = new SameStruct1InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameStruct1InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameStruct1Interface[] unwrapArray(SameStruct1InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameStruct1Interface[] out = new ISameStruct1Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameStruct1Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceParcelable.java b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceParcelable.java new file mode 100644 index 0000000..da8d263 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_messenger/src/main/java/tbSame2/tbSame2_android_messenger/SameStruct2InterfaceParcelable.java @@ -0,0 +1,70 @@ +package tbSame2.tbSame2_android_messenger; + +import tbSame2.tbSame2_api.ISameStruct2Interface; +import android.os.Parcel; +import android.os.Parcelable; +import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_api.Struct2; + + public class SameStruct2InterfaceParcelable implements Parcelable { + + public ISameStruct2Interface data; + + public SameStruct2InterfaceParcelable(ISameStruct2Interface data) { + this.data = data; + } + + public ISameStruct2Interface getSameStruct2Interface() + { + return data; + } + + protected SameStruct2InterfaceParcelable(Parcel in) { + Struct2Parcelable l_parcelableprop1 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + Struct2Parcelable l_parcelableprop2 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SameStruct2InterfaceParcelable createFromParcel(Parcel in) { + return new SameStruct2InterfaceParcelable(in); + } + + @Override + public SameStruct2InterfaceParcelable[] newArray(int size) { + return new SameStruct2InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Struct2Parcelable(data.getProp1()), flags); + dest.writeParcelable(new Struct2Parcelable(data.getProp2()), flags); + + + } + public static SameStruct2InterfaceParcelable[] wrapArray(ISameStruct2Interface[] elements) { + if (elements == null) return null; + SameStruct2InterfaceParcelable[] out = new SameStruct2InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SameStruct2InterfaceParcelable(elements[i]); + } + return out; + } + + public static ISameStruct2Interface[] unwrapArray(SameStruct2InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISameStruct2Interface[] out = new ISameStruct2Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSameStruct2Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle b/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle index c205d81..9f460c7 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle +++ b/goldenmaster/tbSame2/tbSame2_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbSame2_api') - implementation project(':tbSame2_impl') - implementation project(':tbSame2_android_messenger') + api project(':tbSame2_impl') + api project(':tbSame2_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/tbSame2/tbSame2_android_service/build.gradle b/goldenmaster/tbSame2/tbSame2_android_service/build.gradle index 0d434a3..b7b46a0 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/build.gradle +++ b/goldenmaster/tbSame2/tbSame2_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "tbSame2" version = "1.0.0" - android { namespace 'tbSame2.tbSame2_android_service' compileSdk 35 diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java index 304e398..68e9bdf 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java @@ -14,14 +14,8 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; import tbSame2.tbSame2_api.Enum1; import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; import tbSame2.tbSame2_android_service.ISameEnum1InterfaceServiceFactory; @@ -37,10 +31,11 @@ public class SameEnum1InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameEnum1Interface mBackendService; private static ISameEnum1InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +50,19 @@ public static ISameEnum1Interface setService(ISameEnum1InterfaceServiceFactory f { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameEnum1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +73,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameEnum1InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +108,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum1InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -200,7 +219,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java index 6e0c24f..7d1d50f 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java @@ -14,10 +14,6 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; import tbSame2.tbSame2_api.Enum1; import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; @@ -37,10 +33,11 @@ public class SameEnum2InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameEnum2Interface mBackendService; private static ISameEnum2InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +52,19 @@ public static ISameEnum2Interface setService(ISameEnum2InterfaceServiceFactory f { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameEnum2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +75,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameEnum2InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +110,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameEnum2InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -209,7 +230,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); @@ -238,8 +260,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Enum1 param1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java index 03cf9b3..b9a9b78 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java @@ -16,12 +16,6 @@ import android.util.Log; import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_android_service.ISameStruct1InterfaceServiceFactory; @@ -37,10 +31,11 @@ public class SameStruct1InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameStruct1Interface mBackendService; private static ISameStruct1InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +50,19 @@ public static ISameStruct1Interface setService(ISameStruct1InterfaceServiceFacto { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameStruct1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +73,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameStruct1InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +108,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct1InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -200,7 +219,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java index cc6f4f8..120eff6 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java @@ -18,10 +18,6 @@ import tbSame2.tbSame2_android_messenger.Struct1Parcelable; import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_android_messenger.Struct2Parcelable; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; import tbSame2.tbSame2_android_service.ISameStruct2InterfaceServiceFactory; @@ -37,10 +33,11 @@ public class SameStruct2InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISameStruct2Interface mBackendService; private static ISameStruct2InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +52,19 @@ public static ISameStruct2Interface setService(ISameStruct2InterfaceServiceFacto { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SameStruct2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +75,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SameStruct2InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +110,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SameStruct2InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -209,7 +230,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); @@ -238,8 +260,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Struct1 param1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java index 07818a3..17560b0 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -165,7 +165,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Bundle data = response.getData(); Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); assertEquals(receivedprop1, initprop1); } @@ -204,7 +205,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -252,7 +252,8 @@ public void whenNotifiedsig1() assertEquals(SameEnum1InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java index 04b6b30..9cd942e 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -170,8 +170,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Enum1 receivedprop1 = data.getParcelable("prop1", Enum1Parcelable.class).getEnum1(); Enum2 receivedprop2 = data.getParcelable("prop2", Enum2Parcelable.class).getEnum2(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); assertEquals(receivedprop1, initprop1); assertEquals(receivedprop2, initprop2); @@ -211,7 +211,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -246,7 +245,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -294,7 +292,8 @@ public void whenNotifiedsig1() assertEquals(SameEnum2InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); @@ -313,8 +312,8 @@ public void whenNotifiedsig2() assertEquals(SameEnum2InterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); Enum1 receivedparam1 = data.getParcelable("param1", Enum1Parcelable.class).getEnum1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java index 1e66a5f..6b96ccb 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -166,7 +166,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Bundle data = response.getData(); Struct1 receivedprop1 = data.getParcelable("prop1", Struct1Parcelable.class).getStruct1(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); } @@ -205,7 +206,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -253,7 +253,8 @@ public void whenNotifiedsig1() assertEquals(SameStruct1InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java index f824a2c..296fe2a 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -172,8 +172,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Struct2 receivedprop1 = data.getParcelable("prop1", Struct2Parcelable.class).getStruct2(); Struct2 receivedprop2 = data.getParcelable("prop2", Struct2Parcelable.class).getStruct2(); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct2Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); // assertEquals(receivedprop2, initprop2); @@ -213,7 +213,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -248,7 +247,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -296,7 +294,8 @@ public void whenNotifiedsig1() assertEquals(SameStruct2InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); @@ -315,8 +314,8 @@ public void whenNotifiedsig2() assertEquals(SameStruct2InterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); - data.setClassLoader(Struct2Parcelable.class.getClassLoader()); + + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); Struct1 receivedparam1 = data.getParcelable("param1", Struct1Parcelable.class).getStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java index 9c3a0b6..535d3b8 100644 --- a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java @@ -2,7 +2,6 @@ import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; import tbSame2.tbSame2_api.ISameEnum1Interface; -//TODO imported/extern modules import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java index f1b6159..a32ff4f 100644 --- a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java @@ -2,7 +2,6 @@ import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; import tbSame2.tbSame2_api.ISameEnum2Interface; -//TODO imported/extern modules import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java index 600480e..8715dbe 100644 --- a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java @@ -2,7 +2,6 @@ import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct1Interface; -//TODO imported/extern modules import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java index 43f5234..f553fe6 100644 --- a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java @@ -2,7 +2,6 @@ import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct2Interface; -//TODO imported/extern modules import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; diff --git a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java index 220726c..ef062de 100644 --- a/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Arrays; - public class TbSame2TestHelper { @@ -26,4 +25,36 @@ static public Struct2 makeTestStruct2() return testStruct; } + static public ISameStruct1Interface makeTestSameStruct1Interface(ISameStruct1Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + Struct1 localprop1 = TbSame2TestHelper.makeTestStruct1(); + testObjToFill.setProp1(localprop1); + return testObjToFill; + } + + static public ISameStruct2Interface makeTestSameStruct2Interface(ISameStruct2Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + Struct2 localprop1 = TbSame2TestHelper.makeTestStruct2(); + testObjToFill.setProp1(localprop1); + Struct2 localprop2 = TbSame2TestHelper.makeTestStruct2(); + testObjToFill.setProp2(localprop2); + return testObjToFill; + } + + static public ISameEnum1Interface makeTestSameEnum1Interface(ISameEnum1Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setProp1(Enum1.Value2); + return testObjToFill; + } + + static public ISameEnum2Interface makeTestSameEnum2Interface(ISameEnum2Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setProp1(Enum1.Value2); + testObjToFill.setProp2(Enum2.Value2); + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_client_example/build.gradle b/goldenmaster/tbSame2/tbSame2_client_example/build.gradle index a80198d..ce41936 100644 --- a/goldenmaster/tbSame2/tbSame2_client_example/build.gradle +++ b/goldenmaster/tbSame2/tbSame2_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "tbSame2" +version = "1.0.0" + android { namespace 'tbSame2.tbSame2_client_example' compileSdk 35 diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java index 77f441b..2b54b5c 100644 --- a/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java @@ -13,17 +13,8 @@ //TODO for each interface there coudl be a tab? now only first one is added import tbSame2.tbSame2_android_client.SameStruct1InterfaceClient; - -//import message type and parcelabe types import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; - import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import java.util.concurrent.CompletableFuture; @@ -234,4 +225,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/tbSame2/tbSame2_impl/additions.gradle b/goldenmaster/tbSame2/tbSame2_impl/additions.gradle index e9b2135..d5eea51 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/additions.gradle +++ b/goldenmaster/tbSame2/tbSame2_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'tbSame2.tbSame2_impl' diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java index 1977094..4021bc2 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java @@ -6,10 +6,7 @@ import tbSame2.tbSame2_api.ISameEnum1Interface; import tbSame2.tbSame2_api.AbstractSameEnum1Interface; import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; import java.util.Map; diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java index e8be9f4..f263275 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java @@ -6,8 +6,6 @@ import tbSame2.tbSame2_api.ISameEnum2Interface; import tbSame2.tbSame2_api.AbstractSameEnum2Interface; import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; import tbSame2.tbSame2_api.Enum2; diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java index 42385d6..d17fde5 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java @@ -7,9 +7,6 @@ import tbSame2.tbSame2_api.AbstractSameStruct1Interface; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; import java.util.Map; diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java index dc905c7..29bf92b 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java @@ -8,8 +8,6 @@ import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; import java.util.Map; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java index 5c92921..7378bfd 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java @@ -5,10 +5,8 @@ import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; import tbSame2.tbSame2_android_client.SameEnum1InterfaceClient; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java index ad0823a..b171439 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java @@ -5,10 +5,10 @@ import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; import tbSame2.tbSame2_android_client.SameEnum2InterfaceClient; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java index ccc6e80..10aa834 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java @@ -6,9 +6,7 @@ import tbSame2.tbSame2_android_client.SameStruct1InterfaceClient; import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java index b324957..9365e4c 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java @@ -6,9 +6,9 @@ import tbSame2.tbSame2_android_client.SameStruct2InterfaceClient; import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java index ba4d5ac..3aa2b43 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java @@ -6,11 +6,8 @@ import tbSame2.tbSame2_api.ISameEnum1Interface; import tbSame2.tbSame2_api.AbstractSameEnum1Interface; import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; - +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java index d1b0216..0901473 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java @@ -6,11 +6,10 @@ import tbSame2.tbSame2_api.ISameEnum2Interface; import tbSame2.tbSame2_api.AbstractSameEnum2Interface; import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; -import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; import tbSame2.tbSame2_api.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; - +import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java index 2fdb3fb..cfcf6b4 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java @@ -7,10 +7,7 @@ import tbSame2.tbSame2_api.AbstractSameStruct1Interface; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_api.Struct1; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; - +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java index 0602e12..a1b7f15 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java @@ -7,10 +7,9 @@ import tbSame2.tbSame2_api.AbstractSameStruct2Interface; import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; import tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_api.Enum2; - +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle b/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle index dd8b303..9be6ed4 100644 --- a/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle +++ b/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "tbSame2" +version = "1.0.0" + + android { namespace 'tbSame2.tbSame2serviceexample' compileSdk 35 diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java index 3afba59..5356408 100644 --- a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java @@ -20,12 +20,6 @@ //import message type and parcelabe types import tbSame2.tbSame2_api.Struct1; import tbSame2.tbSame2_android_messenger.Struct1Parcelable; -import tbSame2.tbSame2_api.Struct2; -import tbSame2.tbSame2_android_messenger.Struct2Parcelable; -import tbSame2.tbSame2_api.Enum1; -import tbSame2.tbSame2_android_messenger.Enum1Parcelable; -import tbSame2.tbSame2_api.Enum2; -import tbSame2.tbSame2_android_messenger.Enum2Parcelable; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct1Interface; diff --git a/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle b/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle index 43cf463..52f0d68 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle +++ b/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbSimple_api') diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java index 10334cf..687e67c 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java @@ -217,12 +217,14 @@ public void handleMessage(Message msg) case SIG_SigVoid: { Bundle data = msg.getData(); + onSigVoid(); break; } case SIG_SigBool: { Bundle data = msg.getData(); + boolean paramBool = data.getBoolean("paramBool", false); onSigBool(paramBool); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java index c4c6e58..211f01e 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java @@ -189,12 +189,14 @@ public void handleMessage(Message msg) case SIG_SigVoid: { Bundle data = msg.getData(); + onSigVoid(); break; } case SIG_SigBool: { Bundle data = msg.getData(); + boolean paramBool = data.getBoolean("paramBool", false); onSigBool(paramBool); @@ -203,6 +205,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -220,6 +223,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java index 20e58ea..e18546a 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java @@ -217,6 +217,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -234,6 +235,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java index 208b5d5..fb1a6e0 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java @@ -315,6 +315,7 @@ public void handleMessage(Message msg) case SIG_SigBool: { Bundle data = msg.getData(); + boolean[] paramBool = data.getBooleanArray("paramBool"); onSigBool(paramBool); @@ -323,6 +324,7 @@ public void handleMessage(Message msg) case SIG_SigInt: { Bundle data = msg.getData(); + int[] paramInt = data.getIntArray("paramInt"); onSigInt(paramInt); @@ -331,6 +333,7 @@ public void handleMessage(Message msg) case SIG_SigInt32: { Bundle data = msg.getData(); + int[] paramInt32 = data.getIntArray("paramInt32"); onSigInt32(paramInt32); @@ -339,6 +342,7 @@ public void handleMessage(Message msg) case SIG_SigInt64: { Bundle data = msg.getData(); + long[] paramInt64 = data.getLongArray("paramInt64"); onSigInt64(paramInt64); @@ -347,6 +351,7 @@ public void handleMessage(Message msg) case SIG_SigFloat: { Bundle data = msg.getData(); + float[] paramFloat = data.getFloatArray("paramFloat"); onSigFloat(paramFloat); @@ -355,6 +360,7 @@ public void handleMessage(Message msg) case SIG_SigFloat32: { Bundle data = msg.getData(); + float[] paramFloa32 = data.getFloatArray("paramFloa32"); onSigFloat32(paramFloa32); @@ -363,6 +369,7 @@ public void handleMessage(Message msg) case SIG_SigFloat64: { Bundle data = msg.getData(); + double[] paramFloat64 = data.getDoubleArray("paramFloat64"); onSigFloat64(paramFloat64); @@ -371,6 +378,7 @@ public void handleMessage(Message msg) case SIG_SigString: { Bundle data = msg.getData(); + String[] paramString = data.getStringArray("paramString"); onSigString(paramString); @@ -379,6 +387,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -396,6 +405,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -413,6 +423,7 @@ public void handleMessage(Message msg) case RPC_FuncInt32Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -430,6 +441,7 @@ public void handleMessage(Message msg) case RPC_FuncInt64Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -447,6 +459,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -464,6 +477,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat32Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -481,6 +495,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat64Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -498,6 +513,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java index 4c3960f..ad2dcec 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java @@ -301,6 +301,7 @@ public void handleMessage(Message msg) case SIG_SigBool: { Bundle data = msg.getData(); + boolean paramBool = data.getBoolean("paramBool", false); onSigBool(paramBool); @@ -309,6 +310,7 @@ public void handleMessage(Message msg) case SIG_SigInt: { Bundle data = msg.getData(); + int paramInt = data.getInt("paramInt", 0); onSigInt(paramInt); @@ -317,6 +319,7 @@ public void handleMessage(Message msg) case SIG_SigInt32: { Bundle data = msg.getData(); + int paramInt32 = data.getInt("paramInt32", 0); onSigInt32(paramInt32); @@ -325,6 +328,7 @@ public void handleMessage(Message msg) case SIG_SigInt64: { Bundle data = msg.getData(); + long paramInt64 = data.getLong("paramInt64", 0L); onSigInt64(paramInt64); @@ -333,6 +337,7 @@ public void handleMessage(Message msg) case SIG_SigFloat: { Bundle data = msg.getData(); + float paramFloat = data.getFloat("paramFloat", 0.0f); onSigFloat(paramFloat); @@ -341,6 +346,7 @@ public void handleMessage(Message msg) case SIG_SigFloat32: { Bundle data = msg.getData(); + float paramFloat32 = data.getFloat("paramFloat32", 0.0f); onSigFloat32(paramFloat32); @@ -349,6 +355,7 @@ public void handleMessage(Message msg) case SIG_SigFloat64: { Bundle data = msg.getData(); + double paramFloat64 = data.getDouble("paramFloat64", 0.0); onSigFloat64(paramFloat64); @@ -357,6 +364,7 @@ public void handleMessage(Message msg) case SIG_SigString: { Bundle data = msg.getData(); + String paramString = data.getString("paramString", new String()); onSigString(paramString); @@ -365,6 +373,7 @@ public void handleMessage(Message msg) case RPC_FuncNoReturnValueResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -382,6 +391,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -399,6 +409,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -416,6 +427,7 @@ public void handleMessage(Message msg) case RPC_FuncInt32Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -433,6 +445,7 @@ public void handleMessage(Message msg) case RPC_FuncInt64Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -450,6 +463,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -467,6 +481,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat32Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -484,6 +499,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat64Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -501,6 +517,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java index e7f3c15..7a5a4a5 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java @@ -189,12 +189,14 @@ public void handleMessage(Message msg) case SIG_SigVoid: { Bundle data = msg.getData(); + onSigVoid(); break; } case RPC_FuncVoidResp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java index 4c3bb06..a9f201d 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java @@ -148,7 +148,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -162,7 +161,7 @@ public void onReceivepropBoolPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); } - + @Test public void setPropertyRequestpropBool() { @@ -170,7 +169,6 @@ public void setPropertyRequestpropBool() testedClient.setPropBool(testpropBool); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -180,7 +178,7 @@ public void setPropertyRequestpropBool() boolean receivedpropBool = data.getBoolean("propBool", false); assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly + @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -194,7 +192,7 @@ public void onReceivepropIntPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); } - + @Test public void setPropertyRequestpropInt() { @@ -202,7 +200,6 @@ public void setPropertyRequestpropInt() testedClient.setPropInt(testpropInt); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -212,6 +209,7 @@ public void setPropertyRequestpropInt() int receivedpropInt = data.getInt("propInt", 0); assertEquals(receivedpropInt, testpropInt); } + @Test public void whenNotifiedsigVoid() throws RemoteException { diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java index 8283a47..f7bf3a0 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java @@ -193,6 +193,7 @@ public void onfuncVoidRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NoPropertiesInterfaceMessageType.RPC_FuncVoidReq.getValue(), method_request.what); Bundle data = method_request.getData(); + int returnedCallId = data.getInt("callId", -1); //Prepare response @@ -234,6 +235,7 @@ public void onfuncBoolRequest() throws RemoteException { assertEquals(NoPropertiesInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); int returnedCallId = data.getInt("callId", -1); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java index 2ddd408..a96a0b9 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java @@ -148,7 +148,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -162,7 +161,7 @@ public void onReceivepropBoolPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); } - + @Test public void setPropertyRequestpropBool() { @@ -170,7 +169,6 @@ public void setPropertyRequestpropBool() testedClient.setPropBool(testpropBool); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -180,7 +178,7 @@ public void setPropertyRequestpropBool() boolean receivedpropBool = data.getBoolean("propBool", false); assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly + @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -194,7 +192,7 @@ public void onReceivepropIntPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); } - + @Test public void setPropertyRequestpropInt() { @@ -202,7 +200,6 @@ public void setPropertyRequestpropInt() testedClient.setPropInt(testpropInt); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -212,6 +209,7 @@ public void setPropertyRequestpropInt() int receivedpropInt = data.getInt("propInt", 0); assertEquals(receivedpropInt, testpropInt); } + public void onfuncVoidRequest() throws RemoteException { @@ -233,6 +231,7 @@ public void onfuncVoidRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NoSignalsInterfaceMessageType.RPC_FuncVoidReq.getValue(), method_request.what); Bundle data = method_request.getData(); + int returnedCallId = data.getInt("callId", -1); //Prepare response @@ -274,6 +273,7 @@ public void onfuncBoolRequest() throws RemoteException { assertEquals(NoSignalsInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); int returnedCallId = data.getInt("callId", -1); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java index 51ce211..4f11a71 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java @@ -177,7 +177,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); inOrderEventListener.verify(listenerMock,times(1)).onPropReadOnlyStringChanged(testpropReadOnlyString); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -192,7 +191,7 @@ public void onReceivepropBoolPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); } - + @Test public void setPropertyRequestpropBool() { @@ -201,7 +200,6 @@ public void setPropertyRequestpropBool() testedClient.setPropBool(testpropBool); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -211,7 +209,7 @@ public void setPropertyRequestpropBool() boolean[] receivedpropBool = data.getBooleanArray("propBool"); assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly + @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -226,7 +224,7 @@ public void onReceivepropIntPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); } - + @Test public void setPropertyRequestpropInt() { @@ -235,7 +233,6 @@ public void setPropertyRequestpropInt() testedClient.setPropInt(testpropInt); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -245,7 +242,7 @@ public void setPropertyRequestpropInt() int[] receivedpropInt = data.getIntArray("propInt"); assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly + @Test public void onReceivepropInt32PropertyChangeTest() throws RemoteException { // Create and send message @@ -260,7 +257,7 @@ public void onReceivepropInt32PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropInt32Changed(testpropInt32); } - + @Test public void setPropertyRequestpropInt32() { @@ -269,7 +266,6 @@ public void setPropertyRequestpropInt32() testedClient.setPropInt32(testpropInt32); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -279,7 +275,7 @@ public void setPropertyRequestpropInt32() int[] receivedpropInt32 = data.getIntArray("propInt32"); assertEquals(receivedpropInt32, testpropInt32); } -//TODO do not add when a property is readonly + @Test public void onReceivepropInt64PropertyChangeTest() throws RemoteException { // Create and send message @@ -294,7 +290,7 @@ public void onReceivepropInt64PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropInt64Changed(testpropInt64); } - + @Test public void setPropertyRequestpropInt64() { @@ -303,7 +299,6 @@ public void setPropertyRequestpropInt64() testedClient.setPropInt64(testpropInt64); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -313,7 +308,7 @@ public void setPropertyRequestpropInt64() long[] receivedpropInt64 = data.getLongArray("propInt64"); assertEquals(receivedpropInt64, testpropInt64); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -328,7 +323,7 @@ public void onReceivepropFloatPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(testpropFloat); } - + @Test public void setPropertyRequestpropFloat() { @@ -337,7 +332,6 @@ public void setPropertyRequestpropFloat() testedClient.setPropFloat(testpropFloat); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -347,7 +341,7 @@ public void setPropertyRequestpropFloat() float[] receivedpropFloat = data.getFloatArray("propFloat"); assertEquals(receivedpropFloat, testpropFloat); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { // Create and send message @@ -362,7 +356,7 @@ public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloat32Changed(testpropFloat32); } - + @Test public void setPropertyRequestpropFloat32() { @@ -371,7 +365,6 @@ public void setPropertyRequestpropFloat32() testedClient.setPropFloat32(testpropFloat32); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -381,7 +374,7 @@ public void setPropertyRequestpropFloat32() float[] receivedpropFloat32 = data.getFloatArray("propFloat32"); assertEquals(receivedpropFloat32, testpropFloat32); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { // Create and send message @@ -396,7 +389,7 @@ public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); } - + @Test public void setPropertyRequestpropFloat64() { @@ -405,7 +398,6 @@ public void setPropertyRequestpropFloat64() testedClient.setPropFloat64(testpropFloat64); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -415,7 +407,7 @@ public void setPropertyRequestpropFloat64() double[] receivedpropFloat64 = data.getDoubleArray("propFloat64"); assertEquals(receivedpropFloat64, testpropFloat64); } -//TODO do not add when a property is readonly + @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -430,7 +422,7 @@ public void onReceivepropStringPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); } - + @Test public void setPropertyRequestpropString() { @@ -439,7 +431,6 @@ public void setPropertyRequestpropString() testedClient.setPropString(testpropString); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -449,7 +440,7 @@ public void setPropertyRequestpropString() String[] receivedpropString = data.getStringArray("propString"); assertEquals(receivedpropString, testpropString); } -//TODO do not add when a property is readonly + @Test public void onReceivepropReadOnlyStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -463,24 +454,6 @@ public void onReceivepropReadOnlyStringPropertyChangeTest() throws RemoteExcepti Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropReadOnlyStringChanged(testpropReadOnlyString); } - - @Test - public void setPropertyRequestpropReadOnlyString() - { - String testpropReadOnlyString = new String("xyz"); - - testedClient.setPropReadOnlyString(testpropReadOnlyString); - Robolectric.flushForegroundThreadScheduler(); - - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); - Message response = messageCaptor.getValue(); - - assertEquals(SimpleArrayInterfaceMessageType.PROP_PropReadOnlyString.getValue(), response.what); - Bundle data = response.getData(); - - String receivedpropReadOnlyString = data.getString("propReadOnlyString", new String()); - assertEquals(receivedpropReadOnlyString, testpropReadOnlyString); - } @Test public void whenNotifiedsigBool() throws RemoteException { @@ -644,6 +617,7 @@ public void onfuncBoolRequest() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean[] receivedparamBool = data.getBooleanArray("paramBool"); assertEquals(receivedparamBool, testparamBool); int returnedCallId = data.getInt("callId", -1); @@ -690,6 +664,7 @@ public void onfuncIntRequest() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); Bundle data = method_request.getData(); + int[] receivedparamInt = data.getIntArray("paramInt"); assertEquals(receivedparamInt, testparamInt); int returnedCallId = data.getInt("callId", -1); @@ -736,6 +711,7 @@ public void onfuncInt32Request() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncInt32Req.getValue(), method_request.what); Bundle data = method_request.getData(); + int[] receivedparamInt32 = data.getIntArray("paramInt32"); assertEquals(receivedparamInt32, testparamInt32); int returnedCallId = data.getInt("callId", -1); @@ -782,6 +758,7 @@ public void onfuncInt64Request() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncInt64Req.getValue(), method_request.what); Bundle data = method_request.getData(); + long[] receivedparamInt64 = data.getLongArray("paramInt64"); assertEquals(receivedparamInt64, testparamInt64); int returnedCallId = data.getInt("callId", -1); @@ -828,6 +805,7 @@ public void onfuncFloatRequest() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); Bundle data = method_request.getData(); + float[] receivedparamFloat = data.getFloatArray("paramFloat"); assertEquals(receivedparamFloat, testparamFloat); int returnedCallId = data.getInt("callId", -1); @@ -874,6 +852,7 @@ public void onfuncFloat32Request() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloat32Req.getValue(), method_request.what); Bundle data = method_request.getData(); + float[] receivedparamFloat32 = data.getFloatArray("paramFloat32"); assertEquals(receivedparamFloat32, testparamFloat32); int returnedCallId = data.getInt("callId", -1); @@ -920,6 +899,7 @@ public void onfuncFloat64Request() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncFloat64Req.getValue(), method_request.what); Bundle data = method_request.getData(); + double[] receivedparamFloat = data.getDoubleArray("paramFloat"); assertEquals(receivedparamFloat, testparamFloat); int returnedCallId = data.getInt("callId", -1); @@ -966,6 +946,7 @@ public void onfuncStringRequest() throws RemoteException { assertEquals(SimpleArrayInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); Bundle data = method_request.getData(); + String[] receivedparamString = data.getStringArray("paramString"); assertEquals(receivedparamString, testparamString); int returnedCallId = data.getInt("callId", -1); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java index 55438b0..77f1ed6 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java @@ -166,7 +166,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -180,7 +179,7 @@ public void onReceivepropBoolPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(testpropBool); } - + @Test public void setPropertyRequestpropBool() { @@ -188,7 +187,6 @@ public void setPropertyRequestpropBool() testedClient.setPropBool(testpropBool); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -198,7 +196,7 @@ public void setPropertyRequestpropBool() boolean receivedpropBool = data.getBoolean("propBool", false); assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly + @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -212,7 +210,7 @@ public void onReceivepropIntPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(testpropInt); } - + @Test public void setPropertyRequestpropInt() { @@ -220,7 +218,6 @@ public void setPropertyRequestpropInt() testedClient.setPropInt(testpropInt); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -230,7 +227,7 @@ public void setPropertyRequestpropInt() int receivedpropInt = data.getInt("propInt", 0); assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly + @Test public void onReceivepropInt32PropertyChangeTest() throws RemoteException { // Create and send message @@ -244,7 +241,7 @@ public void onReceivepropInt32PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropInt32Changed(testpropInt32); } - + @Test public void setPropertyRequestpropInt32() { @@ -252,7 +249,6 @@ public void setPropertyRequestpropInt32() testedClient.setPropInt32(testpropInt32); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -262,7 +258,7 @@ public void setPropertyRequestpropInt32() int receivedpropInt32 = data.getInt("propInt32", 0); assertEquals(receivedpropInt32, testpropInt32); } -//TODO do not add when a property is readonly + @Test public void onReceivepropInt64PropertyChangeTest() throws RemoteException { // Create and send message @@ -276,7 +272,7 @@ public void onReceivepropInt64PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropInt64Changed(testpropInt64); } - + @Test public void setPropertyRequestpropInt64() { @@ -284,7 +280,6 @@ public void setPropertyRequestpropInt64() testedClient.setPropInt64(testpropInt64); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -294,7 +289,7 @@ public void setPropertyRequestpropInt64() long receivedpropInt64 = data.getLong("propInt64", 0L); assertEquals(receivedpropInt64, testpropInt64); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -308,7 +303,7 @@ public void onReceivepropFloatPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(testpropFloat); } - + @Test public void setPropertyRequestpropFloat() { @@ -316,7 +311,6 @@ public void setPropertyRequestpropFloat() testedClient.setPropFloat(testpropFloat); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -326,7 +320,7 @@ public void setPropertyRequestpropFloat() float receivedpropFloat = data.getFloat("propFloat", 0.0f); assertEquals(receivedpropFloat, testpropFloat, 1e-6f); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { // Create and send message @@ -340,7 +334,7 @@ public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloat32Changed(testpropFloat32); } - + @Test public void setPropertyRequestpropFloat32() { @@ -348,7 +342,6 @@ public void setPropertyRequestpropFloat32() testedClient.setPropFloat32(testpropFloat32); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -358,7 +351,7 @@ public void setPropertyRequestpropFloat32() float receivedpropFloat32 = data.getFloat("propFloat32", 0.0f); assertEquals(receivedpropFloat32, testpropFloat32, 1e-6f); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { // Create and send message @@ -372,7 +365,7 @@ public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloat64Changed(testpropFloat64); } - + @Test public void setPropertyRequestpropFloat64() { @@ -380,7 +373,6 @@ public void setPropertyRequestpropFloat64() testedClient.setPropFloat64(testpropFloat64); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -390,7 +382,7 @@ public void setPropertyRequestpropFloat64() double receivedpropFloat64 = data.getDouble("propFloat64", 0.0); assertEquals(receivedpropFloat64, testpropFloat64, 1e-6f); } -//TODO do not add when a property is readonly + @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -404,7 +396,7 @@ public void onReceivepropStringPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(testpropString); } - + @Test public void setPropertyRequestpropString() { @@ -412,7 +404,6 @@ public void setPropertyRequestpropString() testedClient.setPropString(testpropString); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -422,6 +413,7 @@ public void setPropertyRequestpropString() String receivedpropString = data.getString("propString", new String()); assertEquals(receivedpropString, testpropString); } + @Test public void whenNotifiedsigBool() throws RemoteException { @@ -573,6 +565,7 @@ public void onfuncNoReturnValueRequest() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncNoReturnValueReq.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); int returnedCallId = data.getInt("callId", -1); @@ -616,6 +609,7 @@ public void onfuncBoolRequest() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); Bundle data = method_request.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); int returnedCallId = data.getInt("callId", -1); @@ -660,6 +654,7 @@ public void onfuncIntRequest() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); Bundle data = method_request.getData(); + int receivedparamInt = data.getInt("paramInt", 0); assertEquals(receivedparamInt, testparamInt); int returnedCallId = data.getInt("callId", -1); @@ -704,6 +699,7 @@ public void onfuncInt32Request() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncInt32Req.getValue(), method_request.what); Bundle data = method_request.getData(); + int receivedparamInt32 = data.getInt("paramInt32", 0); assertEquals(receivedparamInt32, testparamInt32); int returnedCallId = data.getInt("callId", -1); @@ -748,6 +744,7 @@ public void onfuncInt64Request() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncInt64Req.getValue(), method_request.what); Bundle data = method_request.getData(); + long receivedparamInt64 = data.getLong("paramInt64", 0L); assertEquals(receivedparamInt64, testparamInt64); int returnedCallId = data.getInt("callId", -1); @@ -792,6 +789,7 @@ public void onfuncFloatRequest() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); Bundle data = method_request.getData(); + float receivedparamFloat = data.getFloat("paramFloat", 0.0f); assertEquals(receivedparamFloat, testparamFloat, 1e-6f); int returnedCallId = data.getInt("callId", -1); @@ -836,6 +834,7 @@ public void onfuncFloat32Request() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncFloat32Req.getValue(), method_request.what); Bundle data = method_request.getData(); + float receivedparamFloat32 = data.getFloat("paramFloat32", 0.0f); assertEquals(receivedparamFloat32, testparamFloat32, 1e-6f); int returnedCallId = data.getInt("callId", -1); @@ -880,6 +879,7 @@ public void onfuncFloat64Request() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncFloat64Req.getValue(), method_request.what); Bundle data = method_request.getData(); + double receivedparamFloat = data.getDouble("paramFloat", 0.0); assertEquals(receivedparamFloat, testparamFloat, 1e-6f); int returnedCallId = data.getInt("callId", -1); @@ -924,6 +924,7 @@ public void onfuncStringRequest() throws RemoteException { assertEquals(SimpleInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); Bundle data = method_request.getData(); + String receivedparamString = data.getString("paramString", new String()); assertEquals(receivedparamString, testparamString); int returnedCallId = data.getInt("callId", -1); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java index 880fb84..0971dc7 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java @@ -177,6 +177,7 @@ public void onfuncVoidRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(VoidInterfaceMessageType.RPC_FuncVoidReq.getValue(), method_request.what); Bundle data = method_request.getData(); + int returnedCallId = data.getInt("callId", -1); //Prepare response diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceParcelable.java new file mode 100644 index 0000000..45680ec --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/EmptyInterfaceParcelable.java @@ -0,0 +1,62 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.IEmptyInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class EmptyInterfaceParcelable implements Parcelable { + + public IEmptyInterface data; + + public EmptyInterfaceParcelable(IEmptyInterface data) { + this.data = data; + } + + public IEmptyInterface getEmptyInterface() + { + return data; + } + + protected EmptyInterfaceParcelable(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public EmptyInterfaceParcelable createFromParcel(Parcel in) { + return new EmptyInterfaceParcelable(in); + } + + @Override + public EmptyInterfaceParcelable[] newArray(int size) { + return new EmptyInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + + } + public static EmptyInterfaceParcelable[] wrapArray(IEmptyInterface[] elements) { + if (elements == null) return null; + EmptyInterfaceParcelable[] out = new EmptyInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new EmptyInterfaceParcelable(elements[i]); + } + return out; + } + + public static IEmptyInterface[] unwrapArray(EmptyInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IEmptyInterface[] out = new IEmptyInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEmptyInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceParcelable.java new file mode 100644 index 0000000..d5f1b1f --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoOperationsInterfaceParcelable.java @@ -0,0 +1,66 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.INoOperationsInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class NoOperationsInterfaceParcelable implements Parcelable { + + public INoOperationsInterface data; + + public NoOperationsInterfaceParcelable(INoOperationsInterface data) { + this.data = data; + } + + public INoOperationsInterface getNoOperationsInterface() + { + return data; + } + + protected NoOperationsInterfaceParcelable(Parcel in) { + data.setPropBool(in.readBoolean()); + data.setPropInt(in.readInt()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NoOperationsInterfaceParcelable createFromParcel(Parcel in) { + return new NoOperationsInterfaceParcelable(in); + } + + @Override + public NoOperationsInterfaceParcelable[] newArray(int size) { + return new NoOperationsInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(data.getPropBool()); + dest.writeInt(data.getPropInt()); + + + } + public static NoOperationsInterfaceParcelable[] wrapArray(INoOperationsInterface[] elements) { + if (elements == null) return null; + NoOperationsInterfaceParcelable[] out = new NoOperationsInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NoOperationsInterfaceParcelable(elements[i]); + } + return out; + } + + public static INoOperationsInterface[] unwrapArray(NoOperationsInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + INoOperationsInterface[] out = new INoOperationsInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNoOperationsInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceParcelable.java new file mode 100644 index 0000000..11678a7 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoPropertiesInterfaceParcelable.java @@ -0,0 +1,62 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.INoPropertiesInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class NoPropertiesInterfaceParcelable implements Parcelable { + + public INoPropertiesInterface data; + + public NoPropertiesInterfaceParcelable(INoPropertiesInterface data) { + this.data = data; + } + + public INoPropertiesInterface getNoPropertiesInterface() + { + return data; + } + + protected NoPropertiesInterfaceParcelable(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public NoPropertiesInterfaceParcelable createFromParcel(Parcel in) { + return new NoPropertiesInterfaceParcelable(in); + } + + @Override + public NoPropertiesInterfaceParcelable[] newArray(int size) { + return new NoPropertiesInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + + } + public static NoPropertiesInterfaceParcelable[] wrapArray(INoPropertiesInterface[] elements) { + if (elements == null) return null; + NoPropertiesInterfaceParcelable[] out = new NoPropertiesInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NoPropertiesInterfaceParcelable(elements[i]); + } + return out; + } + + public static INoPropertiesInterface[] unwrapArray(NoPropertiesInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + INoPropertiesInterface[] out = new INoPropertiesInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNoPropertiesInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceParcelable.java new file mode 100644 index 0000000..a8ec557 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/NoSignalsInterfaceParcelable.java @@ -0,0 +1,66 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.INoSignalsInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class NoSignalsInterfaceParcelable implements Parcelable { + + public INoSignalsInterface data; + + public NoSignalsInterfaceParcelable(INoSignalsInterface data) { + this.data = data; + } + + public INoSignalsInterface getNoSignalsInterface() + { + return data; + } + + protected NoSignalsInterfaceParcelable(Parcel in) { + data.setPropBool(in.readBoolean()); + data.setPropInt(in.readInt()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NoSignalsInterfaceParcelable createFromParcel(Parcel in) { + return new NoSignalsInterfaceParcelable(in); + } + + @Override + public NoSignalsInterfaceParcelable[] newArray(int size) { + return new NoSignalsInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(data.getPropBool()); + dest.writeInt(data.getPropInt()); + + + } + public static NoSignalsInterfaceParcelable[] wrapArray(INoSignalsInterface[] elements) { + if (elements == null) return null; + NoSignalsInterfaceParcelable[] out = new NoSignalsInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NoSignalsInterfaceParcelable(elements[i]); + } + return out; + } + + public static INoSignalsInterface[] unwrapArray(NoSignalsInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + INoSignalsInterface[] out = new INoSignalsInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNoSignalsInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceParcelable.java new file mode 100644 index 0000000..092e673 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleArrayInterfaceParcelable.java @@ -0,0 +1,80 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class SimpleArrayInterfaceParcelable implements Parcelable { + + public ISimpleArrayInterface data; + + public SimpleArrayInterfaceParcelable(ISimpleArrayInterface data) { + this.data = data; + } + + public ISimpleArrayInterface getSimpleArrayInterface() + { + return data; + } + + protected SimpleArrayInterfaceParcelable(Parcel in) { + data.setPropBool(in.createBooleanArray()); + data.setPropInt(in.createIntArray()); + data.setPropInt32(in.createIntArray()); + data.setPropInt64(in.createLongArray()); + data.setPropFloat(in.createFloatArray()); + data.setPropFloat32(in.createFloatArray()); + data.setPropFloat64(in.createDoubleArray()); + data.setPropString(in.createStringArray()); + data.setPropReadOnlyString(in.readString()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SimpleArrayInterfaceParcelable createFromParcel(Parcel in) { + return new SimpleArrayInterfaceParcelable(in); + } + + @Override + public SimpleArrayInterfaceParcelable[] newArray(int size) { + return new SimpleArrayInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBooleanArray(data.getPropBool()); + dest.writeIntArray(data.getPropInt()); + dest.writeIntArray(data.getPropInt32()); + dest.writeLongArray(data.getPropInt64()); + dest.writeFloatArray(data.getPropFloat()); + dest.writeFloatArray(data.getPropFloat32()); + dest.writeDoubleArray(data.getPropFloat64()); + dest.writeStringArray(data.getPropString()); + dest.writeString(data.getPropReadOnlyString()); + + + } + public static SimpleArrayInterfaceParcelable[] wrapArray(ISimpleArrayInterface[] elements) { + if (elements == null) return null; + SimpleArrayInterfaceParcelable[] out = new SimpleArrayInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SimpleArrayInterfaceParcelable(elements[i]); + } + return out; + } + + public static ISimpleArrayInterface[] unwrapArray(SimpleArrayInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISimpleArrayInterface[] out = new ISimpleArrayInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSimpleArrayInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceParcelable.java new file mode 100644 index 0000000..bd47708 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceParcelable.java @@ -0,0 +1,78 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.ISimpleInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class SimpleInterfaceParcelable implements Parcelable { + + public ISimpleInterface data; + + public SimpleInterfaceParcelable(ISimpleInterface data) { + this.data = data; + } + + public ISimpleInterface getSimpleInterface() + { + return data; + } + + protected SimpleInterfaceParcelable(Parcel in) { + data.setPropBool(in.readBoolean()); + data.setPropInt(in.readInt()); + data.setPropInt32(in.readInt()); + data.setPropInt64(in.readLong()); + data.setPropFloat(in.readFloat()); + data.setPropFloat32(in.readFloat()); + data.setPropFloat64(in.readDouble()); + data.setPropString(in.readString()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SimpleInterfaceParcelable createFromParcel(Parcel in) { + return new SimpleInterfaceParcelable(in); + } + + @Override + public SimpleInterfaceParcelable[] newArray(int size) { + return new SimpleInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(data.getPropBool()); + dest.writeInt(data.getPropInt()); + dest.writeInt(data.getPropInt32()); + dest.writeLong(data.getPropInt64()); + dest.writeFloat(data.getPropFloat()); + dest.writeFloat(data.getPropFloat32()); + dest.writeDouble(data.getPropFloat64()); + dest.writeString(data.getPropString()); + + + } + public static SimpleInterfaceParcelable[] wrapArray(ISimpleInterface[] elements) { + if (elements == null) return null; + SimpleInterfaceParcelable[] out = new SimpleInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new SimpleInterfaceParcelable(elements[i]); + } + return out; + } + + public static ISimpleInterface[] unwrapArray(SimpleInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + ISimpleInterface[] out = new ISimpleInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getSimpleInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceParcelable.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceParcelable.java new file mode 100644 index 0000000..d1ffa3c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/VoidInterfaceParcelable.java @@ -0,0 +1,62 @@ +package tbSimple.tbSimple_android_messenger; + +import tbSimple.tbSimple_api.IVoidInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class VoidInterfaceParcelable implements Parcelable { + + public IVoidInterface data; + + public VoidInterfaceParcelable(IVoidInterface data) { + this.data = data; + } + + public IVoidInterface getVoidInterface() + { + return data; + } + + protected VoidInterfaceParcelable(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public VoidInterfaceParcelable createFromParcel(Parcel in) { + return new VoidInterfaceParcelable(in); + } + + @Override + public VoidInterfaceParcelable[] newArray(int size) { + return new VoidInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + + } + public static VoidInterfaceParcelable[] wrapArray(IVoidInterface[] elements) { + if (elements == null) return null; + VoidInterfaceParcelable[] out = new VoidInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new VoidInterfaceParcelable(elements[i]); + } + return out; + } + + public static IVoidInterface[] unwrapArray(VoidInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IVoidInterface[] out = new IVoidInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getVoidInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle b/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle index 64f796a..f0e18ee 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle +++ b/goldenmaster/tbSimple/tbSimple_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':tbSimple_api') - implementation project(':tbSimple_impl') - implementation project(':tbSimple_android_messenger') + api project(':tbSimple_impl') + api project(':tbSimple_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/tbSimple/tbSimple_android_service/build.gradle b/goldenmaster/tbSimple/tbSimple_android_service/build.gradle index 7a9715a..f0caa25 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/build.gradle +++ b/goldenmaster/tbSimple/tbSimple_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "tbSimple" version = "1.0.0" - android { namespace 'tbSimple.tbSimple_android_service' compileSdk 35 diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java index 523669a..00eb14e 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class EmptyInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static IEmptyInterface mBackendService; private static IEmptyInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static IEmptyInterface setService(IEmptyInterfaceServiceFactory factory) { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(EmptyInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(EmptyInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(EmptyInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(EmptyInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(EmptyInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java index 11afffe..0964139 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class NoOperationsInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INoOperationsInterface mBackendService; private static INoOperationsInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static INoOperationsInterface setService(INoOperationsInterfaceServiceFac { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NoOperationsInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NoOperationsInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NoOperationsInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NoOperationsInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NoOperationsInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java index 7698db8..aa49592 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class NoPropertiesInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INoPropertiesInterface mBackendService; private static INoPropertiesInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static INoPropertiesInterface setService(INoPropertiesInterfaceServiceFac { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NoPropertiesInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NoPropertiesInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NoPropertiesInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NoPropertiesInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NoPropertiesInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -183,6 +208,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); mBackendService.funcVoid(); @@ -207,6 +233,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean paramBool = data.getBoolean("paramBool", false); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java index 8254b3d..0a3ff1b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class NoSignalsInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INoSignalsInterface mBackendService; private static INoSignalsInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static INoSignalsInterface setService(INoSignalsInterfaceServiceFactory f { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NoSignalsInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NoSignalsInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NoSignalsInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NoSignalsInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NoSignalsInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -199,6 +224,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); mBackendService.funcVoid(); @@ -223,6 +249,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean paramBool = data.getBoolean("paramBool", false); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java index 075ae9a..e70eefe 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class SimpleArrayInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISimpleArrayInterface mBackendService; private static ISimpleArrayInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static ISimpleArrayInterface setService(ISimpleArrayInterfaceServiceFacto { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SimpleArrayInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SimpleArrayInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SimpleArrayInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleArrayInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SimpleArrayInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -255,6 +280,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean[] paramBool = data.getBooleanArray("paramBool"); @@ -283,6 +309,7 @@ public void handleMessage(Message msg) case RPC_FuncIntReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int[] paramInt = data.getIntArray("paramInt"); @@ -311,6 +338,7 @@ public void handleMessage(Message msg) case RPC_FuncInt32Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int[] paramInt32 = data.getIntArray("paramInt32"); @@ -339,6 +367,7 @@ public void handleMessage(Message msg) case RPC_FuncInt64Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); long[] paramInt64 = data.getLongArray("paramInt64"); @@ -367,6 +396,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); float[] paramFloat = data.getFloatArray("paramFloat"); @@ -395,6 +425,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat32Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); float[] paramFloat32 = data.getFloatArray("paramFloat32"); @@ -423,6 +454,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat64Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); double[] paramFloat = data.getDoubleArray("paramFloat"); @@ -451,6 +483,7 @@ public void handleMessage(Message msg) case RPC_FuncStringReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); String[] paramString = data.getStringArray("paramString"); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java index 3bbbb89..9ce874b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class SimpleInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static ISimpleInterface mBackendService; private static ISimpleInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static ISimpleInterface setService(ISimpleInterfaceServiceFactory factory { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(SimpleInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(SimpleInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(SimpleInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(SimpleInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(SimpleInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -247,6 +272,7 @@ public void handleMessage(Message msg) case RPC_FuncNoReturnValueReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean paramBool = data.getBoolean("paramBool", false); @@ -273,6 +299,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); boolean paramBool = data.getBoolean("paramBool", false); @@ -301,6 +328,7 @@ public void handleMessage(Message msg) case RPC_FuncIntReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int paramInt = data.getInt("paramInt", 0); @@ -329,6 +357,7 @@ public void handleMessage(Message msg) case RPC_FuncInt32Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int paramInt32 = data.getInt("paramInt32", 0); @@ -357,6 +386,7 @@ public void handleMessage(Message msg) case RPC_FuncInt64Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); long paramInt64 = data.getLong("paramInt64", 0L); @@ -385,6 +415,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); float paramFloat = data.getFloat("paramFloat", 0.0f); @@ -413,6 +444,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat32Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); float paramFloat32 = data.getFloat("paramFloat32", 0.0f); @@ -441,6 +473,7 @@ public void handleMessage(Message msg) case RPC_FuncFloat64Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); double paramFloat = data.getDouble("paramFloat", 0.0); @@ -469,6 +502,7 @@ public void handleMessage(Message msg) case RPC_FuncStringReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); String paramString = data.getString("paramString", new String()); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java index 0c213be..73e1cb2 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java @@ -29,10 +29,11 @@ public class VoidInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static IVoidInterface mBackendService; private static IVoidInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -47,10 +48,19 @@ public static IVoidInterface setService(IVoidInterfaceServiceFactory factory) { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(VoidInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -61,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(VoidInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -84,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(VoidInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(VoidInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(VoidInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -183,6 +208,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidReq: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); mBackendService.funcVoid(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java index 1dd1ab0..009c9e9 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java @@ -152,6 +152,7 @@ void registerFakeActivityClient(Messenger messenger, String id) assertEquals(EmptyInterfaceMessageType.INIT.getValue(), response.what); Bundle data = response.getData(); + } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java index 2d48308..4ed51c6 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java @@ -162,6 +162,7 @@ void registerFakeActivityClient(Messenger messenger, String id) boolean receivedpropBool = data.getBoolean("propBool", false); int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropBool, initpropBool); assertEquals(receivedpropInt, initpropInt); @@ -201,7 +202,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -236,7 +236,6 @@ public void whenNotifiedpropBool() assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -283,6 +282,7 @@ public void whenNotifiedsigVoid() assertEquals(NoOperationsInterfaceMessageType.SIG_SigVoid.getValue(), response.what); Bundle data = response.getData(); + } @Test public void whenNotifiedsigBool() @@ -298,6 +298,7 @@ public void whenNotifiedsigBool() assertEquals(NoOperationsInterfaceMessageType.SIG_SigBool.getValue(), response.what); Bundle data = response.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java index a5b1c17..1813cfa 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java @@ -152,6 +152,7 @@ void registerFakeActivityClient(Messenger messenger, String id) assertEquals(NoPropertiesInterfaceMessageType.INIT.getValue(), response.what); Bundle data = response.getData(); + } @@ -201,6 +202,7 @@ public void whenNotifiedsigVoid() assertEquals(NoPropertiesInterfaceMessageType.SIG_SigVoid.getValue(), response.what); Bundle data = response.getData(); + } @Test public void whenNotifiedsigBool() @@ -216,6 +218,7 @@ public void whenNotifiedsigBool() assertEquals(NoPropertiesInterfaceMessageType.SIG_SigBool.getValue(), response.what); Bundle data = response.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java index 265e6dd..419325f 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java @@ -162,6 +162,7 @@ void registerFakeActivityClient(Messenger messenger, String id) boolean receivedpropBool = data.getBoolean("propBool", false); int receivedpropInt = data.getInt("propInt", 0); + assertEquals(receivedpropBool, initpropBool); assertEquals(receivedpropInt, initpropInt); @@ -201,7 +202,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -236,7 +236,6 @@ public void whenNotifiedpropBool() assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java index 02ed697..266d40c 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java @@ -213,6 +213,7 @@ void registerFakeActivityClient(Messenger messenger, String id) String[] receivedpropString = data.getStringArray("propString"); String receivedpropReadOnlyString = data.getString("propReadOnlyString", new String()); + assertEquals(receivedpropBool, initpropBool); assertEquals(receivedpropInt, initpropInt); assertEquals(receivedpropInt32, initpropInt32); @@ -259,7 +260,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -296,7 +296,6 @@ public void whenNotifiedpropBool() assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -333,7 +332,6 @@ public void whenNotifiedpropInt() assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly @Test public void onReceivepropInt32PropertyChangeTest() throws RemoteException { // Create and send message @@ -370,7 +368,6 @@ public void whenNotifiedpropInt32() assertEquals(receivedpropInt32, testpropInt32); } -//TODO do not add when a property is readonly @Test public void onReceivepropInt64PropertyChangeTest() throws RemoteException { // Create and send message @@ -407,7 +404,6 @@ public void whenNotifiedpropInt64() assertEquals(receivedpropInt64, testpropInt64); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -444,7 +440,6 @@ public void whenNotifiedpropFloat() assertEquals(receivedpropFloat, testpropFloat); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { // Create and send message @@ -481,7 +476,6 @@ public void whenNotifiedpropFloat32() assertEquals(receivedpropFloat32, testpropFloat32); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { // Create and send message @@ -518,7 +512,6 @@ public void whenNotifiedpropFloat64() assertEquals(receivedpropFloat64, testpropFloat64); } -//TODO do not add when a property is readonly @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -555,21 +548,6 @@ public void whenNotifiedpropString() assertEquals(receivedpropString, testpropString); } -//TODO do not add when a property is readonly - @Test - public void onReceivepropReadOnlyStringPropertyChangeTest() throws RemoteException { - // Create and send message - Message msg = Message.obtain(null, SimpleArrayInterfaceMessageType.PROP_PropReadOnlyString.getValue()); - Bundle data = new Bundle(); - String testpropReadOnlyString = new String("xyz"); - data.putString("propReadOnlyString", testpropReadOnlyString); - - msg.setData(data); - mServiceMessenger.send(msg); - Robolectric.flushForegroundThreadScheduler(); - inOrderBackendService.verify(backendServiceMock,times(1)).setPropReadOnlyString(testpropReadOnlyString); - - } @Test public void whenNotifiedpropReadOnlyString() @@ -605,6 +583,7 @@ public void whenNotifiedsigBool() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigBool.getValue(), response.what); Bundle data = response.getData(); + boolean[] receivedparamBool = data.getBooleanArray("paramBool"); assertEquals(receivedparamBool, testparamBool); } @@ -623,6 +602,7 @@ public void whenNotifiedsigInt() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigInt.getValue(), response.what); Bundle data = response.getData(); + int[] receivedparamInt = data.getIntArray("paramInt"); assertEquals(receivedparamInt, testparamInt); } @@ -641,6 +621,7 @@ public void whenNotifiedsigInt32() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigInt32.getValue(), response.what); Bundle data = response.getData(); + int[] receivedparamInt32 = data.getIntArray("paramInt32"); assertEquals(receivedparamInt32, testparamInt32); } @@ -659,6 +640,7 @@ public void whenNotifiedsigInt64() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigInt64.getValue(), response.what); Bundle data = response.getData(); + long[] receivedparamInt64 = data.getLongArray("paramInt64"); assertEquals(receivedparamInt64, testparamInt64); } @@ -677,6 +659,7 @@ public void whenNotifiedsigFloat() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigFloat.getValue(), response.what); Bundle data = response.getData(); + float[] receivedparamFloat = data.getFloatArray("paramFloat"); assertEquals(receivedparamFloat, testparamFloat); } @@ -695,6 +678,7 @@ public void whenNotifiedsigFloat32() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigFloat32.getValue(), response.what); Bundle data = response.getData(); + float[] receivedparamFloa32 = data.getFloatArray("paramFloa32"); assertEquals(receivedparamFloa32, testparamFloa32); } @@ -713,6 +697,7 @@ public void whenNotifiedsigFloat64() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigFloat64.getValue(), response.what); Bundle data = response.getData(); + double[] receivedparamFloat64 = data.getDoubleArray("paramFloat64"); assertEquals(receivedparamFloat64, testparamFloat64); } @@ -731,6 +716,7 @@ public void whenNotifiedsigString() assertEquals(SimpleArrayInterfaceMessageType.SIG_SigString.getValue(), response.what); Bundle data = response.getData(); + String[] receivedparamString = data.getStringArray("paramString"); assertEquals(receivedparamString, testparamString); } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java index 161eaa7..aa0ffc5 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java @@ -192,6 +192,7 @@ void registerFakeActivityClient(Messenger messenger, String id) double receivedpropFloat64 = data.getDouble("propFloat64", 0.0); String receivedpropString = data.getString("propString", new String()); + assertEquals(receivedpropBool, initpropBool); assertEquals(receivedpropInt, initpropInt); assertEquals(receivedpropInt32, initpropInt32); @@ -240,7 +241,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -275,7 +275,6 @@ public void whenNotifiedpropBool() assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -310,7 +309,6 @@ public void whenNotifiedpropInt() assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly @Test public void onReceivepropInt32PropertyChangeTest() throws RemoteException { // Create and send message @@ -345,7 +343,6 @@ public void whenNotifiedpropInt32() assertEquals(receivedpropInt32, testpropInt32); } -//TODO do not add when a property is readonly @Test public void onReceivepropInt64PropertyChangeTest() throws RemoteException { // Create and send message @@ -380,7 +377,6 @@ public void whenNotifiedpropInt64() assertEquals(receivedpropInt64, testpropInt64); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -416,7 +412,6 @@ public void whenNotifiedpropFloat() assertEquals(receivedpropFloat, testpropFloat, 1e-6f); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloat32PropertyChangeTest() throws RemoteException { // Create and send message @@ -452,7 +447,6 @@ public void whenNotifiedpropFloat32() assertEquals(receivedpropFloat32, testpropFloat32, 1e-6f); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloat64PropertyChangeTest() throws RemoteException { // Create and send message @@ -488,7 +482,6 @@ public void whenNotifiedpropFloat64() assertEquals(receivedpropFloat64, testpropFloat64, 1e-6f); } -//TODO do not add when a property is readonly @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -537,6 +530,7 @@ public void whenNotifiedsigBool() assertEquals(SimpleInterfaceMessageType.SIG_SigBool.getValue(), response.what); Bundle data = response.getData(); + boolean receivedparamBool = data.getBoolean("paramBool", false); assertEquals(receivedparamBool, testparamBool); } @@ -554,6 +548,7 @@ public void whenNotifiedsigInt() assertEquals(SimpleInterfaceMessageType.SIG_SigInt.getValue(), response.what); Bundle data = response.getData(); + int receivedparamInt = data.getInt("paramInt", 0); assertEquals(receivedparamInt, testparamInt); } @@ -571,6 +566,7 @@ public void whenNotifiedsigInt32() assertEquals(SimpleInterfaceMessageType.SIG_SigInt32.getValue(), response.what); Bundle data = response.getData(); + int receivedparamInt32 = data.getInt("paramInt32", 0); assertEquals(receivedparamInt32, testparamInt32); } @@ -588,6 +584,7 @@ public void whenNotifiedsigInt64() assertEquals(SimpleInterfaceMessageType.SIG_SigInt64.getValue(), response.what); Bundle data = response.getData(); + long receivedparamInt64 = data.getLong("paramInt64", 0L); assertEquals(receivedparamInt64, testparamInt64); } @@ -605,6 +602,7 @@ public void whenNotifiedsigFloat() assertEquals(SimpleInterfaceMessageType.SIG_SigFloat.getValue(), response.what); Bundle data = response.getData(); + float receivedparamFloat = data.getFloat("paramFloat", 0.0f); assertEquals(receivedparamFloat, testparamFloat, 1e-6f); @@ -623,6 +621,7 @@ public void whenNotifiedsigFloat32() assertEquals(SimpleInterfaceMessageType.SIG_SigFloat32.getValue(), response.what); Bundle data = response.getData(); + float receivedparamFloat32 = data.getFloat("paramFloat32", 0.0f); assertEquals(receivedparamFloat32, testparamFloat32, 1e-6f); @@ -641,6 +640,7 @@ public void whenNotifiedsigFloat64() assertEquals(SimpleInterfaceMessageType.SIG_SigFloat64.getValue(), response.what); Bundle data = response.getData(); + double receivedparamFloat64 = data.getDouble("paramFloat64", 0.0); assertEquals(receivedparamFloat64, testparamFloat64, 1e-6f); @@ -659,6 +659,7 @@ public void whenNotifiedsigString() assertEquals(SimpleInterfaceMessageType.SIG_SigString.getValue(), response.what); Bundle data = response.getData(); + String receivedparamString = data.getString("paramString", new String()); assertEquals(receivedparamString, testparamString); } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java index 3d7a6c5..157259e 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java @@ -152,6 +152,7 @@ void registerFakeActivityClient(Messenger messenger, String id) assertEquals(VoidInterfaceMessageType.INIT.getValue(), response.what); Bundle data = response.getData(); + } @@ -201,6 +202,7 @@ public void whenNotifiedsigVoid() assertEquals(VoidInterfaceMessageType.SIG_SigVoid.getValue(), response.what); Bundle data = response.getData(); + } diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java index aa11f79..e8a3b29 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; import tbSimple.tbSimple_api.IEmptyInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java index bf330cc..67d104c 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; import tbSimple.tbSimple_api.INoOperationsInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java index 306aaef..d23f8c7 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; import tbSimple.tbSimple_api.INoPropertiesInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java index 3ef0003..7fe1beb 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; import tbSimple.tbSimple_api.INoSignalsInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java index bd60111..8e8b4c3 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; import tbSimple.tbSimple_api.ISimpleArrayInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java index 7102f7b..13614b0 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; import tbSimple.tbSimple_api.ISimpleInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java index 4e471a2..9006dff 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java @@ -2,7 +2,6 @@ import tbSimple.tbSimple_api.IVoidInterfaceEventListener; import tbSimple.tbSimple_api.IVoidInterface; -//TODO imported/extern modules import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java index e712661..24df438 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java @@ -4,8 +4,85 @@ import java.util.Objects; import java.util.Arrays; - public class TbSimpleTestHelper { + static public IVoidInterface makeTestVoidInterface(IVoidInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + return testObjToFill; + } + + static public ISimpleInterface makeTestSimpleInterface(ISimpleInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setPropBool(true); + testObjToFill.setPropInt(1); + testObjToFill.setPropInt32(1); + testObjToFill.setPropInt64(1L); + testObjToFill.setPropFloat(1.0f); + testObjToFill.setPropFloat32(1.0f); + testObjToFill.setPropFloat64(1.0); + testObjToFill.setPropString(new String("xyz")); + return testObjToFill; + } + + static public ISimpleArrayInterface makeTestSimpleArrayInterface(ISimpleArrayInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + boolean[] localpropBool = new boolean[1]; + localpropBool[0] = true; + testObjToFill.setPropBool(localpropBool); + int[] localpropInt = new int[1]; + localpropInt[0] = 1; + testObjToFill.setPropInt(localpropInt); + int[] localpropInt32 = new int[1]; + localpropInt32[0] = 1; + testObjToFill.setPropInt32(localpropInt32); + long[] localpropInt64 = new long[1]; + localpropInt64[0] = 1L; + testObjToFill.setPropInt64(localpropInt64); + float[] localpropFloat = new float[1]; + localpropFloat[0] = 1.0f; + testObjToFill.setPropFloat(localpropFloat); + float[] localpropFloat32 = new float[1]; + localpropFloat32[0] = 1.0f; + testObjToFill.setPropFloat32(localpropFloat32); + double[] localpropFloat64 = new double[1]; + localpropFloat64[0] = 1.0; + testObjToFill.setPropFloat64(localpropFloat64); + String[] localpropString = new String[1]; + localpropString[0] = new String("xyz"); + testObjToFill.setPropString(localpropString); + testObjToFill.setPropReadOnlyString(new String("xyz")); + return testObjToFill; + } + + static public INoPropertiesInterface makeTestNoPropertiesInterface(INoPropertiesInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + return testObjToFill; + } + + static public INoOperationsInterface makeTestNoOperationsInterface(INoOperationsInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setPropBool(true); + testObjToFill.setPropInt(1); + return testObjToFill; + } + + static public INoSignalsInterface makeTestNoSignalsInterface(INoSignalsInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setPropBool(true); + testObjToFill.setPropInt(1); + return testObjToFill; + } + + static public IEmptyInterface makeTestEmptyInterface(IEmptyInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_client_example/build.gradle b/goldenmaster/tbSimple/tbSimple_client_example/build.gradle index 8f02747..f09f20f 100644 --- a/goldenmaster/tbSimple/tbSimple_client_example/build.gradle +++ b/goldenmaster/tbSimple/tbSimple_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "tbSimple" +version = "1.0.0" + android { namespace 'tbSimple.tbSimple_client_example' compileSdk 35 diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java index 10c83d1..dd3bb15 100644 --- a/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java @@ -13,9 +13,6 @@ //TODO for each interface there coudl be a tab? now only first one is added import tbSimple.tbSimple_android_client.VoidInterfaceClient; - -//import message type and parcelabe types - import tbSimple.tbSimple_api.IVoidInterfaceEventListener; import java.util.concurrent.CompletableFuture; @@ -208,4 +205,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/tbSimple/tbSimple_impl/additions.gradle b/goldenmaster/tbSimple/tbSimple_impl/additions.gradle index 8793152..897e0e4 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/additions.gradle +++ b/goldenmaster/tbSimple/tbSimple_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'tbSimple.tbSimple_impl' diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java index d46b8ad..a4ca654 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractEmptyInterface; import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java index 3e5590b..ac36257 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractNoOperationsInterface; import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java index fa105a5..ae66365 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractNoPropertiesInterface; import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java index bea3154..aaa8e6a 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractNoSignalsInterface; import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java index e3ad193..f5c4cb6 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractSimpleArrayInterface; import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java index 252325a..7d9a3c9 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractSimpleInterface; import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java index 3a30620..7a6bae8 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java @@ -7,7 +7,6 @@ import tbSimple.tbSimple_api.AbstractVoidInterface; import tbSimple.tbSimple_api.IVoidInterfaceEventListener; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle b/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle index 3a8bce2..1711e21 100644 --- a/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "tbSimple" +version = "1.0.0" + + android { namespace 'tbSimple.tbSimpleserviceexample' compileSdk 35 diff --git a/goldenmaster/testbed1/testbed1_android_client/additions.gradle b/goldenmaster/testbed1/testbed1_android_client/additions.gradle index b37559e..6d71b2c 100644 --- a/goldenmaster/testbed1/testbed1_android_client/additions.gradle +++ b/goldenmaster/testbed1/testbed1_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':testbed1_api') diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java new file mode 100644 index 0000000..e2298f9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java @@ -0,0 +1,846 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_client; + +import android.content.ServiceConnection; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.content.ComponentName; +import android.util.Log; + +//import message type and parcelabe types +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; + + +public class StructArray2InterfaceClient extends AbstractStructArray2Interface implements ServiceConnection +{ + private static final String TAG = "StructArray2InterfaceClient"; + + private final Context mApplicationContext; + + private boolean mIsBoundToService = false; + private Messenger mServiceMessenger = null; + private Messenger mClientMessenger = null; + private String mConnectionId; + private ClientHandler mClientHandler = new ClientHandler(); + + private final Map> mpendingCalls = new ConcurrentHashMap<>(); + AtomicInteger callIdsGetter = new AtomicInteger(0); + private StructBoolWithArray m_propBool = new StructBoolWithArray(); + private StructIntWithArray m_propInt = new StructIntWithArray(); + private StructFloatWithArray m_propFloat = new StructFloatWithArray(); + private StructStringWithArray m_propString = new StructStringWithArray(); + private StructEnumWithArray m_propEnum = new StructEnumWithArray(); + + + public StructArray2InterfaceClient(Context applicationContext, String connectionId) + { + assert (applicationContext != null); + mApplicationContext = applicationContext; + + if (connectionId.isEmpty()) + { + mConnectionId = UUID.randomUUID().toString(); + } + else + { + mConnectionId = connectionId; + } + mClientMessenger = new Messenger(mClientHandler); + } + + public boolean isBoundToService() + { + return mIsBoundToService; + } + + + /** + * Binds to a running service of type StructArray2InterfaceServiceAdapter. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bindToService(String packageName) + { + Intent intent = new Intent(); + intent.setClassName(packageName, "testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter"); + intent.putExtra("connectionID", mConnectionId); + Log.d(TAG, "Using context: " + mApplicationContext.getClass().getName()); + Log.d(TAG, "bindToService intent=" + intent + ", mServiceConnection=" + this); + + return mApplicationContext.bindService(intent, this,0 ); + } + + /** + * Unbinds from the service instance. + */ + public void unbindFromService() + { + if (mIsBoundToService) + { + Log.v(TAG, "unbindFromService"); + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + mApplicationContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder serviceBinder) + { + Log.v(TAG, "onServiceConnected name=" + name + ", serviceBinder=" + serviceBinder); + // Retrieve and use the Messenger + mServiceMessenger = new Messenger(serviceBinder); + mIsBoundToService = true; + + requestRegisterClient(); + fire_readyStatusChanged(true); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + Log.w(TAG, "onServiceDisconnected name=" + name); + doCleanupForUnbinding("onServiceDisconnected name=" + name); + } + + @Override + public void onBindingDied(ComponentName name) + { + Log.w(TAG, "onBindingDied name=" + name); + doCleanupForUnbinding("onBindingDied name=" + name); + ServiceConnection.super.onBindingDied(name); + } + + private void requestRegisterClient() + { + if (mClientMessenger != null) + { + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + msg.replyTo = mClientMessenger; + msg.getData().putString("connectionID", mConnectionId); + mClientHandler.sendToService(msg); + } + } + + private void doCleanupForUnbinding(String caller) + { + mServiceMessenger = null; + mClientMessenger = null; + mIsBoundToService = false; + fire_readyStatusChanged(false); + } + + + class ClientHandler extends Handler { + + ClientHandler(){ + super(Looper.getMainLooper()); + } + + public void sendToService(Message msg) + { + if (mServiceMessenger != null) + { + try + { + mServiceMessenger.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send message to service, looks like it is not correctly connected, make sure client has bind to service " + e); + } + } + } + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch (StructArray2InterfaceMessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + + StructBoolWithArray propBool = data.getParcelable("propBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + onPropBool(propBool); + + StructIntWithArray propInt = data.getParcelable("propInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + onPropInt(propInt); + + StructFloatWithArray propFloat = data.getParcelable("propFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + onPropFloat(propFloat); + + StructStringWithArray propString = data.getParcelable("propString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + onPropString(propString); + + StructEnumWithArray propEnum = data.getParcelable("propEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + onPropEnum(propEnum); + + break; + } + case SET_PropBool: + { + Bundle data = msg.getData(); + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + + StructBoolWithArray propBool = data.getParcelable("propBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + + onPropBool(propBool); + break; + } + case SET_PropInt: + { + Bundle data = msg.getData(); + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + + + StructIntWithArray propInt = data.getParcelable("propInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + + onPropInt(propInt); + break; + } + case SET_PropFloat: + { + Bundle data = msg.getData(); + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + + + StructFloatWithArray propFloat = data.getParcelable("propFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + + onPropFloat(propFloat); + break; + } + case SET_PropString: + { + Bundle data = msg.getData(); + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + + + StructStringWithArray propString = data.getParcelable("propString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + + onPropString(propString); + break; + } + case SET_PropEnum: + { + Bundle data = msg.getData(); + data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + + + StructEnumWithArray propEnum = data.getParcelable("propEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + + onPropEnum(propEnum); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case SIG_SigBool: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + StructBoolWithArray paramBool = data.getParcelable("paramBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + onSigBool(paramBool); + break; + } + case SIG_SigInt: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + + StructIntWithArray paramInt = data.getParcelable("paramInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + onSigInt(paramInt); + break; + } + case SIG_SigFloat: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + + StructFloatWithArray paramFloat = data.getParcelable("paramFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + onSigFloat(paramFloat); + break; + } + case SIG_SigString: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + + StructStringWithArray paramString = data.getParcelable("paramString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + onSigString(paramString); + break; + } + case RPC_FuncBoolResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArray2InterfaceMessageType.RPC_FuncBoolResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncIntResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArray2InterfaceMessageType.RPC_FuncIntResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncFloatResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArray2InterfaceMessageType.RPC_FuncFloatResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncStringResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArray2InterfaceMessageType.RPC_FuncStringResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncEnumResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArray2InterfaceMessageType.RPC_FuncEnumResp , could not find pending call for " + msg.obj); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + + } + }; + @Override + public void setPropBool(StructBoolWithArray propBool) + { + Log.i(TAG, "request setPropBool called "+ propBool); + if (! m_propBool.equals(propBool)) + { + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.PROP_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propBool", new StructBoolWithArrayParcelable(propBool)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropBool(StructBoolWithArray propBool) + { + Log.i(TAG, "value received from service for PropBool "); + if (! m_propBool.equals(propBool)) + { + m_propBool = propBool; + firePropBoolChanged(propBool); + } + + } + + @Override + public StructBoolWithArray getPropBool() + { + Log.i(TAG, "request getPropBool called, returning local"); + return m_propBool; + } + + + @Override + public void setPropInt(StructIntWithArray propInt) + { + Log.i(TAG, "request setPropInt called "+ propInt); + if (! m_propInt.equals(propInt)) + { + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.PROP_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propInt", new StructIntWithArrayParcelable(propInt)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropInt(StructIntWithArray propInt) + { + Log.i(TAG, "value received from service for PropInt "); + if (! m_propInt.equals(propInt)) + { + m_propInt = propInt; + firePropIntChanged(propInt); + } + + } + + @Override + public StructIntWithArray getPropInt() + { + Log.i(TAG, "request getPropInt called, returning local"); + return m_propInt; + } + + + @Override + public void setPropFloat(StructFloatWithArray propFloat) + { + Log.i(TAG, "request setPropFloat called "+ propFloat); + if (! m_propFloat.equals(propFloat)) + { + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.PROP_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propFloat", new StructFloatWithArrayParcelable(propFloat)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropFloat(StructFloatWithArray propFloat) + { + Log.i(TAG, "value received from service for PropFloat "); + if (! m_propFloat.equals(propFloat)) + { + m_propFloat = propFloat; + firePropFloatChanged(propFloat); + } + + } + + @Override + public StructFloatWithArray getPropFloat() + { + Log.i(TAG, "request getPropFloat called, returning local"); + return m_propFloat; + } + + + @Override + public void setPropString(StructStringWithArray propString) + { + Log.i(TAG, "request setPropString called "+ propString); + if (! m_propString.equals(propString)) + { + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.PROP_PropString.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propString", new StructStringWithArrayParcelable(propString)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropString(StructStringWithArray propString) + { + Log.i(TAG, "value received from service for PropString "); + if (! m_propString.equals(propString)) + { + m_propString = propString; + firePropStringChanged(propString); + } + + } + + @Override + public StructStringWithArray getPropString() + { + Log.i(TAG, "request getPropString called, returning local"); + return m_propString; + } + + + @Override + public void setPropEnum(StructEnumWithArray propEnum) + { + Log.i(TAG, "request setPropEnum called "+ propEnum); + if (! m_propEnum.equals(propEnum)) + { + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.PROP_PropEnum.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propEnum", new StructEnumWithArrayParcelable(propEnum)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropEnum(StructEnumWithArray propEnum) + { + Log.i(TAG, "value received from service for PropEnum "); + if (! m_propEnum.equals(propEnum)) + { + m_propEnum = propEnum; + firePropEnumChanged(propEnum); + } + + } + + @Override + public StructEnumWithArray getPropEnum() + { + Log.i(TAG, "request getPropEnum called, returning local"); + return m_propEnum; + } + + + // methods + + + @Override + public StructBool[] funcBool(StructBoolWithArray paramBool) { + CompletableFuture resFuture = funcBoolAsync(paramBool); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcBoolAsync(StructBoolWithArray paramBool) { + + Log.i(TAG, "Call on service funcBool "+ " " + paramBool); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.RPC_FuncBoolReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramBool", new StructBoolWithArrayParcelable(paramBool)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructBool[] result = StructBoolParcelable.unwrapArray((StructBoolParcelable[])bundle.getParcelableArray("result", StructBoolParcelable.class)); + Log.v(TAG, "resolve funcBool" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructInt[] funcInt(StructIntWithArray paramInt) { + CompletableFuture resFuture = funcIntAsync(paramInt); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcIntAsync(StructIntWithArray paramInt) { + + Log.i(TAG, "Call on service funcInt "+ " " + paramInt); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.RPC_FuncIntReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramInt", new StructIntWithArrayParcelable(paramInt)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructInt[] result = StructIntParcelable.unwrapArray((StructIntParcelable[])bundle.getParcelableArray("result", StructIntParcelable.class)); + Log.v(TAG, "resolve funcInt" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructFloat[] funcFloat(StructFloatWithArray paramFloat) { + CompletableFuture resFuture = funcFloatAsync(paramFloat); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcFloatAsync(StructFloatWithArray paramFloat) { + + Log.i(TAG, "Call on service funcFloat "+ " " + paramFloat); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.RPC_FuncFloatReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramFloat", new StructFloatWithArrayParcelable(paramFloat)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructFloat[] result = StructFloatParcelable.unwrapArray((StructFloatParcelable[])bundle.getParcelableArray("result", StructFloatParcelable.class)); + Log.v(TAG, "resolve funcFloat" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public StructString[] funcString(StructStringWithArray paramString) { + CompletableFuture resFuture = funcStringAsync(paramString); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcStringAsync(StructStringWithArray paramString) { + + Log.i(TAG, "Call on service funcString "+ " " + paramString); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.RPC_FuncStringReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramString", new StructStringWithArrayParcelable(paramString)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + StructString[] result = StructStringParcelable.unwrapArray((StructStringParcelable[])bundle.getParcelableArray("result", StructStringParcelable.class)); + Log.v(TAG, "resolve funcString" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public Enum0[] funcEnum(StructEnumWithArray paramEnum) { + CompletableFuture resFuture = funcEnumAsync(paramEnum); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcEnumAsync(StructEnumWithArray paramEnum) { + + Log.i(TAG, "Call on service funcEnum "+ " " + paramEnum); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.RPC_FuncEnumReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("paramEnum", new StructEnumWithArrayParcelable(paramEnum)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum0[] result = Enum0Parcelable.unwrapArray((Enum0Parcelable[])bundle.getParcelableArray("result", Enum0Parcelable.class)); + Log.v(TAG, "resolve funcEnum" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override + public boolean _isReady() { + return mIsBoundToService && mServiceMessenger != null; + } + + // Should be called when message arrives + public void onSigBool(StructBoolWithArray paramBool) + { + Log.i(TAG, "onSigBool received from service"); + fireSigBool(paramBool); + } + public void onSigInt(StructIntWithArray paramInt) + { + Log.i(TAG, "onSigInt received from service"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloatWithArray paramFloat) + { + Log.i(TAG, "onSigFloat received from service"); + fireSigFloat(paramFloat); + } + public void onSigString(StructStringWithArray paramString) + { + Log.i(TAG, "onSigString received from service"); + fireSigString(paramString); + } +} diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java index 8f87b1c..29a7253 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java @@ -17,12 +17,14 @@ import android.util.Log; //import message type and parcelabe types +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.StructBool; import testbed1.testbed1_android_messenger.StructBoolParcelable; -import testbed1.testbed1_api.StructInt; -import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; @@ -59,6 +61,7 @@ public class StructArrayInterfaceClient extends AbstractStructArrayInterface imp private StructInt[] m_propInt = new StructInt[]{}; private StructFloat[] m_propFloat = new StructFloat[]{}; private StructString[] m_propString = new StructString[]{}; + private Enum0[] m_propEnum = new Enum0[]{}; public StructArrayInterfaceClient(Context applicationContext, String connectionId) @@ -191,10 +194,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool[] propBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); @@ -208,6 +208,9 @@ public void handleMessage(Message msg) StructString[] propString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); onPropString(propString); + + Enum0[] propEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + onPropEnum(propEnum); break; } @@ -255,13 +258,25 @@ public void handleMessage(Message msg) onPropString(propString); break; } + case SET_PropEnum: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + + Enum0[] propEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + + onPropEnum(propEnum); + break; + } // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. case SIG_SigBool: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool[] paramBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); onSigBool(paramBool); @@ -270,7 +285,8 @@ public void handleMessage(Message msg) case SIG_SigInt: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); StructInt[] paramInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); onSigInt(paramInt); @@ -279,7 +295,8 @@ public void handleMessage(Message msg) case SIG_SigFloat: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); StructFloat[] paramFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); onSigFloat(paramFloat); @@ -288,16 +305,28 @@ public void handleMessage(Message msg) case SIG_SigString: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); StructString[] paramString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); onSigString(paramString); break; } + case SIG_SigEnum: { + + Bundle data = msg.getData(); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0[] paramEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("paramEnum", Enum0Parcelable.class)); + onSigEnum(paramEnum); + break; + } case RPC_FuncBoolResp: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -315,7 +344,8 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -333,7 +363,8 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -351,7 +382,8 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -365,6 +397,25 @@ public void handleMessage(Message msg) } break; + } + case RPC_FuncEnumResp: { + + Bundle data = msg.getData(); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received StructArrayInterfaceMessageType.RPC_FuncEnumResp , could not find pending call for " + msg.obj); + } + break; + } default: Log.e(TAG, "Receive Unsupported message: " + msg.what); @@ -518,6 +569,42 @@ public StructString[] getPropString() } + @Override + public void setPropEnum(Enum0[] propEnum) + { + Log.i(TAG, "request setPropEnum called "+ propEnum); + if (! Arrays.equals(m_propEnum, propEnum)) + { + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.PROP_PropEnum.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(propEnum)); + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + public void onPropEnum(Enum0[] propEnum) + { + Log.i(TAG, "value received from service for PropEnum "); + if (! Arrays.equals(m_propEnum, propEnum)) + { + m_propEnum = propEnum; + firePropEnumChanged(propEnum); + } + + } + + @Override + public Enum0[] getPropEnum() + { + Log.i(TAG, "request getPropEnum called, returning local"); + return m_propEnum; + } + + // methods @@ -685,6 +772,48 @@ public CompletableFuture funcStringAsync(StructString[] paramSt // Store the lambda function in the map mpendingCalls.put(msgId, resolver); + return future; + } + + + @Override + public Enum0[] funcEnum(Enum0[] paramEnum) { + CompletableFuture resFuture = funcEnumAsync(paramEnum); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcEnumAsync(Enum0[] paramEnum) { + + Log.i(TAG, "Call on service funcEnum "+ " " + paramEnum); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.RPC_FuncEnumReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelableArray("paramEnum", Enum0Parcelable.wrapArray(paramEnum)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + Enum0[] result = Enum0Parcelable.unwrapArray((Enum0Parcelable[])bundle.getParcelableArray("result", Enum0Parcelable.class)); + Log.v(TAG, "resolve funcEnum" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + return future; } @@ -714,4 +843,9 @@ public void onSigString(StructString[] paramString) Log.i(TAG, "onSigString received from service"); fireSigString(paramString); } + public void onSigEnum(Enum0[] paramEnum) + { + Log.i(TAG, "onSigEnum received from service"); + fireSigEnum(paramEnum); + } } diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java index b381158..c3f8ded 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java @@ -19,10 +19,10 @@ //import message type and parcelabe types import testbed1.testbed1_api.StructBool; import testbed1.testbed1_android_messenger.StructBoolParcelable; -import testbed1.testbed1_api.StructInt; -import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; @@ -191,10 +191,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool propBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); @@ -261,7 +258,8 @@ public void handleMessage(Message msg) case SIG_SigBool: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool paramBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); onSigBool(paramBool); @@ -270,7 +268,8 @@ public void handleMessage(Message msg) case SIG_SigInt: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); StructInt paramInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); onSigInt(paramInt); @@ -279,7 +278,8 @@ public void handleMessage(Message msg) case SIG_SigFloat: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); StructFloat paramFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); onSigFloat(paramFloat); @@ -288,7 +288,8 @@ public void handleMessage(Message msg) case SIG_SigString: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); StructString paramString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); onSigString(paramString); @@ -297,7 +298,8 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -315,7 +317,8 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -333,7 +336,8 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -351,7 +355,8 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java new file mode 100644 index 0000000..cce6bfd --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java @@ -0,0 +1,646 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. +package testbed1.testbed1_android_client; + +import testbed1.testbed1_android_client.StructArray2InterfaceClient; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_android_messenger.StructStructParcelable; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_android_messenger.StructEnumParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_android_messenger.StructStructWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; +import testbed1.testbed1_api.Testbed1TestHelper; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceMessageType; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import android.content.ComponentName; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.InOrder; + +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + +import androidx.annotation.NonNull; + + +interface IStructArray2InterfaceClientMessageGetter +{ + public void getMessage(Message msg); +} + + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class StructArray2InterfaceClientTest +{ + + @Mock + private Context mMockContext; + + private StructArray2InterfaceClient testedClient; + private IStructArray2InterfaceEventListener listenerMock = mock(IStructArray2InterfaceEventListener.class); + private Messenger mServiceMessenger; + private Messenger mClientMessenger; + private Handler mServiceHandler ; + private String mTestConnectionID1 = "MyTestClient"; + InOrder inOrderServiceMessenger; + InOrder inOrderEventListener; + + private IStructArray2InterfaceClientMessageGetter serviceMessagesStorage = mock(IStructArray2InterfaceClientMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + @After + public void tearDown() { + + testedClient.unbindFromService(); + + Robolectric.flushForegroundThreadScheduler(); + + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.UNREGISTER_CLIENT.getValue(), register_msg.what); + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + testedClient.removeEventListener(listenerMock); + } + + Handler createServiceHandlerMock(IStructArray2InterfaceClientMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + @Before + public void setUp() throws RemoteException + { + inOrderServiceMessenger = inOrder(serviceMessagesStorage); + inOrderEventListener = inOrder(listenerMock); + mServiceHandler = createServiceHandlerMock(serviceMessagesStorage); + mServiceMessenger = new Messenger(mServiceHandler); + IBinder serviceBinder = mServiceMessenger.getBinder(); + + mMockContext = RuntimeEnvironment.getApplication(); + + + testedClient = new StructArray2InterfaceClient(mMockContext, mTestConnectionID1); + testedClient.addEventListener(listenerMock); + ComponentName componentName = new ComponentName("testbed1.testbed1_android_service", "testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter"); + testedClient.onServiceConnected(componentName, serviceBinder); + + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message register_msg = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.REGISTER_CLIENT.getValue(), register_msg.what); + mClientMessenger = register_msg.replyTo; + assertEquals(mTestConnectionID1, register_msg.getData().getString("connectionID", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + assertTrue(testedClient._isReady()); + } + + @Test + public void onInitReceive() throws RemoteException + { + //PREPARE message + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.INIT.getValue()); + Bundle data = new Bundle(); + StructBoolWithArray testpropBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + data.putParcelable("propBool", new StructBoolWithArrayParcelable(testpropBool)); + StructIntWithArray testpropInt = Testbed1TestHelper.makeTestStructIntWithArray(); + data.putParcelable("propInt", new StructIntWithArrayParcelable(testpropInt)); + StructFloatWithArray testpropFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + data.putParcelable("propFloat", new StructFloatWithArrayParcelable(testpropFloat)); + StructStringWithArray testpropString = Testbed1TestHelper.makeTestStructStringWithArray(); + data.putParcelable("propString", new StructStringWithArrayParcelable(testpropString)); + StructEnumWithArray testpropEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + data.putParcelable("propEnum", new StructEnumWithArrayParcelable(testpropEnum)); + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBoolWithArray.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructIntWithArray.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloatWithArray.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructStringWithArray.class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropEnumChanged(any(StructEnumWithArray.class)); + } + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SET_PropBool.getValue()); + Bundle data = new Bundle(); + StructBoolWithArray testpropBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + data.putParcelable("propBool", new StructBoolWithArrayParcelable(testpropBool)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBoolWithArray.class)); + } + + @Test + public void setPropertyRequestpropBool() + { + StructBoolWithArray testpropBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + + testedClient.setPropBool(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.PROP_PropBool.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + StructBoolWithArray receivedpropBool = data.getParcelable("propBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + assertEquals(receivedpropBool, testpropBool); + } + + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SET_PropInt.getValue()); + Bundle data = new Bundle(); + StructIntWithArray testpropInt = Testbed1TestHelper.makeTestStructIntWithArray(); + data.putParcelable("propInt", new StructIntWithArrayParcelable(testpropInt)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructIntWithArray.class)); + } + + @Test + public void setPropertyRequestpropInt() + { + StructIntWithArray testpropInt = Testbed1TestHelper.makeTestStructIntWithArray(); + + testedClient.setPropInt(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.PROP_PropInt.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + + StructIntWithArray receivedpropInt = data.getParcelable("propInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + assertEquals(receivedpropInt, testpropInt); + } + + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SET_PropFloat.getValue()); + Bundle data = new Bundle(); + StructFloatWithArray testpropFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + data.putParcelable("propFloat", new StructFloatWithArrayParcelable(testpropFloat)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloatWithArray.class)); + } + + @Test + public void setPropertyRequestpropFloat() + { + StructFloatWithArray testpropFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + + testedClient.setPropFloat(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.PROP_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + + StructFloatWithArray receivedpropFloat = data.getParcelable("propFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + assertEquals(receivedpropFloat, testpropFloat); + } + + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SET_PropString.getValue()); + Bundle data = new Bundle(); + StructStringWithArray testpropString = Testbed1TestHelper.makeTestStructStringWithArray(); + data.putParcelable("propString", new StructStringWithArrayParcelable(testpropString)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructStringWithArray.class)); + } + + @Test + public void setPropertyRequestpropString() + { + StructStringWithArray testpropString = Testbed1TestHelper.makeTestStructStringWithArray(); + + testedClient.setPropString(testpropString); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.PROP_PropString.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + + StructStringWithArray receivedpropString = data.getParcelable("propString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + assertEquals(receivedpropString, testpropString); + } + + @Test + public void onReceivepropEnumPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SET_PropEnum.getValue()); + Bundle data = new Bundle(); + StructEnumWithArray testpropEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + data.putParcelable("propEnum", new StructEnumWithArrayParcelable(testpropEnum)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropEnumChanged(any(StructEnumWithArray.class)); + } + + @Test + public void setPropertyRequestpropEnum() + { + StructEnumWithArray testpropEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + + testedClient.setPropEnum(testpropEnum); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.PROP_PropEnum.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + + StructEnumWithArray receivedpropEnum = data.getParcelable("propEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + assertEquals(receivedpropEnum, testpropEnum); + } + + @Test + public void whenNotifiedsigBool() throws RemoteException + { + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SIG_SigBool.getValue()); + Bundle data = new Bundle(); + StructBoolWithArray testparamBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + data.putParcelable("paramBool", new StructBoolWithArrayParcelable(testparamBool)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigBool( any(StructBoolWithArray.class)); + +} + @Test + public void whenNotifiedsigInt() throws RemoteException + { + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SIG_SigInt.getValue()); + Bundle data = new Bundle(); + StructIntWithArray testparamInt = Testbed1TestHelper.makeTestStructIntWithArray(); + data.putParcelable("paramInt", new StructIntWithArrayParcelable(testparamInt)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigInt( any(StructIntWithArray.class)); + +} + @Test + public void whenNotifiedsigFloat() throws RemoteException + { + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SIG_SigFloat.getValue()); + Bundle data = new Bundle(); + StructFloatWithArray testparamFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + data.putParcelable("paramFloat", new StructFloatWithArrayParcelable(testparamFloat)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigFloat( any(StructFloatWithArray.class)); + +} + @Test + public void whenNotifiedsigString() throws RemoteException + { + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.SIG_SigString.getValue()); + Bundle data = new Bundle(); + StructStringWithArray testparamString = Testbed1TestHelper.makeTestStructStringWithArray(); + data.putParcelable("paramString", new StructStringWithArrayParcelable(testparamString)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigString( any(StructStringWithArray.class)); + +} + + + public void onfuncBoolRequest() throws RemoteException { + + // Execute method + StructBoolWithArray testparamBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + StructBool[] expectedResult = new StructBool[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructBool(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcBoolAsync(testparamBool); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + StructBoolWithArray receivedparamBool = data.getParcelable("paramBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + assertEquals(receivedparamBool, testparamBool); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncBoolResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructBoolParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncIntRequest() throws RemoteException { + + // Execute method + StructIntWithArray testparamInt = Testbed1TestHelper.makeTestStructIntWithArray(); + StructInt[] expectedResult = new StructInt[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructInt(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcIntAsync(testparamInt); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + + StructIntWithArray receivedparamInt = data.getParcelable("paramInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + assertEquals(receivedparamInt, testparamInt); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncIntResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructIntParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncFloatRequest() throws RemoteException { + + // Execute method + StructFloatWithArray testparamFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + StructFloat[] expectedResult = new StructFloat[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructFloat(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcFloatAsync(testparamFloat); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + + StructFloatWithArray receivedparamFloat = data.getParcelable("paramFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + assertEquals(receivedparamFloat, testparamFloat); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncFloatResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructFloatParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncStringRequest() throws RemoteException { + + // Execute method + StructStringWithArray testparamString = Testbed1TestHelper.makeTestStructStringWithArray(); + StructString[] expectedResult = new StructString[1]; + expectedResult[0] = Testbed1TestHelper.makeTestStructString(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcStringAsync(testparamString); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + + StructStringWithArray receivedparamString = data.getParcelable("paramString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + assertEquals(receivedparamString, testparamString); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncStringResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", StructStringParcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncEnumRequest() throws RemoteException { + + // Execute method + StructEnumWithArray testparamEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + Enum0[] expectedResult = new Enum0[1]; + expectedResult[0] = Enum0.Value1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcEnumAsync(testparamEnum); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArray2InterfaceMessageType.RPC_FuncEnumReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + + StructEnumWithArray receivedparamEnum = data.getParcelable("paramEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + assertEquals(receivedparamEnum, testparamEnum); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncEnumResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", Enum0Parcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +} diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java index 4abc19e..aa5081f 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java @@ -12,7 +12,25 @@ import testbed1.testbed1_android_messenger.StructFloatParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_android_messenger.StructStructParcelable; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_android_messenger.StructEnumParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_android_messenger.StructStructWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; import testbed1.testbed1_api.Testbed1TestHelper; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.IStructArrayInterfaceEventListener; import testbed1.testbed1_api.IStructArrayInterface; @@ -156,6 +174,9 @@ public void onInitReceive() throws RemoteException StructString[] testpropString = new StructString[1]; testpropString[0] = Testbed1TestHelper.makeTestStructString(); data.putParcelableArray("propString", StructStringParcelable.wrapArray(testpropString)); + Enum0[] testpropEnum = new Enum0[1]; + testpropEnum[0] = Enum0.Value1; + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(testpropEnum)); //setup mock expectations msg.setData(data); @@ -165,8 +186,8 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt[].class)); inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat[].class)); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString[].class)); + inOrderEventListener.verify(listenerMock,times(1)).onPropEnumChanged(testpropEnum); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -181,7 +202,7 @@ public void onReceivepropBoolPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBool[].class)); } - + @Test public void setPropertyRequestpropBool() { @@ -190,7 +211,6 @@ public void setPropertyRequestpropBool() testedClient.setPropBool(testpropBool); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -201,7 +221,7 @@ public void setPropertyRequestpropBool() StructBool[] receivedpropBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("propBool", StructBoolParcelable.class)); assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly + @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -216,7 +236,7 @@ public void onReceivepropIntPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt[].class)); } - + @Test public void setPropertyRequestpropInt() { @@ -225,7 +245,6 @@ public void setPropertyRequestpropInt() testedClient.setPropInt(testpropInt); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -236,7 +255,7 @@ public void setPropertyRequestpropInt() StructInt[] receivedpropInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("propInt", StructIntParcelable.class)); assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -251,7 +270,7 @@ public void onReceivepropFloatPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat[].class)); } - + @Test public void setPropertyRequestpropFloat() { @@ -260,7 +279,6 @@ public void setPropertyRequestpropFloat() testedClient.setPropFloat(testpropFloat); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -271,7 +289,7 @@ public void setPropertyRequestpropFloat() StructFloat[] receivedpropFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); assertEquals(receivedpropFloat, testpropFloat); } -//TODO do not add when a property is readonly + @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -286,7 +304,7 @@ public void onReceivepropStringPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString[].class)); } - + @Test public void setPropertyRequestpropString() { @@ -295,7 +313,6 @@ public void setPropertyRequestpropString() testedClient.setPropString(testpropString); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -306,6 +323,41 @@ public void setPropertyRequestpropString() StructString[] receivedpropString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); assertEquals(receivedpropString, testpropString); } + + @Test + public void onReceivepropEnumPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SET_PropEnum.getValue()); + Bundle data = new Bundle(); + Enum0[] testpropEnum = new Enum0[1]; + testpropEnum[0] = Enum0.Value1; + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(testpropEnum)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderEventListener.verify(listenerMock,times(1)).onPropEnumChanged(testpropEnum); + } + + @Test + public void setPropertyRequestpropEnum() + { + Enum0[] testpropEnum = new Enum0[1]; + testpropEnum[0] = Enum0.Value1; + + testedClient.setPropEnum(testpropEnum); + Robolectric.flushForegroundThreadScheduler(); + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.PROP_PropEnum.getValue(), response.what); + Bundle data = response.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0[] receivedpropEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + assertEquals(receivedpropEnum, testpropEnum); + } + @Test public void whenNotifiedsigBool() throws RemoteException { @@ -373,6 +425,23 @@ public void whenNotifiedsigString() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onSigString( any(StructString[].class)); +} + @Test + public void whenNotifiedsigEnum() throws RemoteException + { + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.SIG_SigEnum.getValue()); + Bundle data = new Bundle(); + Enum0[] testparamEnum = new Enum0[1]; + testparamEnum[0] = Enum0.Value1; + data.putParcelableArray("paramEnum", Enum0Parcelable.wrapArray(testparamEnum)); + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + inOrderEventListener.verify(listenerMock,times(1)).onSigEnum(testparamEnum); + } @@ -400,7 +469,8 @@ public void onfuncBoolRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructArrayInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool[] receivedparamBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); assertEquals(receivedparamBool, testparamBool); @@ -447,7 +517,8 @@ public void onfuncIntRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructArrayInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); StructInt[] receivedparamInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); assertEquals(receivedparamInt, testparamInt); @@ -494,7 +565,8 @@ public void onfuncFloatRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructArrayInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); StructFloat[] receivedparamFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); assertEquals(receivedparamFloat, testparamFloat); @@ -541,7 +613,8 @@ public void onfuncStringRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructArrayInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); StructString[] receivedparamString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); assertEquals(receivedparamString, testparamString); @@ -563,4 +636,52 @@ public void onfuncStringRequest() throws RemoteException { } + + public void onfuncEnumRequest() throws RemoteException { + + // Execute method + Enum0[] testparamEnum = new Enum0[1]; + testparamEnum[0] = Enum0.Value1; + Enum0[] expectedResult = new Enum0[1]; + expectedResult[0] = Enum0.Value1; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcEnumAsync(testparamEnum); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(StructArrayInterfaceMessageType.RPC_FuncEnumReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0[] receivedparamEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("paramEnum", Enum0Parcelable.class)); + assertEquals(receivedparamEnum, testparamEnum); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncEnumResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelableArray("result", Enum0Parcelable.wrapArray(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + } diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java index 8d9ec4f..0085dc1 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java @@ -12,7 +12,25 @@ import testbed1.testbed1_android_messenger.StructFloatParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_android_messenger.StructStructParcelable; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_android_messenger.StructEnumParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_android_messenger.StructStructWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; import testbed1.testbed1_api.Testbed1TestHelper; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.IStructInterfaceEventListener; import testbed1.testbed1_api.IStructInterface; @@ -162,7 +180,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat.class)); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString.class)); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -176,7 +193,7 @@ public void onReceivepropBoolPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropBoolChanged(any(StructBool.class)); } - + @Test public void setPropertyRequestpropBool() { @@ -184,7 +201,6 @@ public void setPropertyRequestpropBool() testedClient.setPropBool(testpropBool); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -195,7 +211,7 @@ public void setPropertyRequestpropBool() StructBool receivedpropBool = data.getParcelable("propBool", StructBoolParcelable.class).getStructBool(); assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly + @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -209,7 +225,7 @@ public void onReceivepropIntPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropIntChanged(any(StructInt.class)); } - + @Test public void setPropertyRequestpropInt() { @@ -217,7 +233,6 @@ public void setPropertyRequestpropInt() testedClient.setPropInt(testpropInt); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -228,7 +243,7 @@ public void setPropertyRequestpropInt() StructInt receivedpropInt = data.getParcelable("propInt", StructIntParcelable.class).getStructInt(); assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly + @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -242,7 +257,7 @@ public void onReceivepropFloatPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropFloatChanged(any(StructFloat.class)); } - + @Test public void setPropertyRequestpropFloat() { @@ -250,7 +265,6 @@ public void setPropertyRequestpropFloat() testedClient.setPropFloat(testpropFloat); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -261,7 +275,7 @@ public void setPropertyRequestpropFloat() StructFloat receivedpropFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); assertEquals(receivedpropFloat, testpropFloat); } -//TODO do not add when a property is readonly + @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -275,7 +289,7 @@ public void onReceivepropStringPropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onPropStringChanged(any(StructString.class)); } - + @Test public void setPropertyRequestpropString() { @@ -283,7 +297,6 @@ public void setPropertyRequestpropString() testedClient.setPropString(testpropString); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -294,6 +307,7 @@ public void setPropertyRequestpropString() StructString receivedpropString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); assertEquals(receivedpropString, testpropString); } + @Test public void whenNotifiedsigBool() throws RemoteException { @@ -382,7 +396,8 @@ public void onfuncBoolRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructInterfaceMessageType.RPC_FuncBoolReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool receivedparamBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); assertEquals(receivedparamBool, testparamBool); @@ -427,7 +442,8 @@ public void onfuncIntRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructInterfaceMessageType.RPC_FuncIntReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); StructInt receivedparamInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); assertEquals(receivedparamInt, testparamInt); @@ -472,7 +488,8 @@ public void onfuncFloatRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructInterfaceMessageType.RPC_FuncFloatReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); StructFloat receivedparamFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); assertEquals(receivedparamFloat, testparamFloat); @@ -517,7 +534,8 @@ public void onfuncStringRequest() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(StructInterfaceMessageType.RPC_FuncStringReq.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); StructString receivedparamString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); assertEquals(receivedparamString, testparamString); diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/Enum0Parcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/Enum0Parcelable.java new file mode 100644 index 0000000..8117110 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/Enum0Parcelable.java @@ -0,0 +1,69 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.Enum0; +import android.os.Parcel; +import android.os.Parcelable; + + + +//TODO imports - may need some struct from this or imported module + + public class Enum0Parcelable implements Parcelable { + + public Enum0 data; + + public Enum0Parcelable(Enum0 data) { + this.data = data; + } + + public Enum0 getEnum0() + { + return data; + } + + protected Enum0Parcelable(Parcel in) { + int intValue = in.readInt(); + this.data = Enum0.fromValue(intValue); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Enum0Parcelable createFromParcel(Parcel in) { + return new Enum0Parcelable(in); + } + + @Override + public Enum0Parcelable[] newArray(int size) { + return new Enum0Parcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(data.getValue()); + } + + public static Enum0Parcelable[] wrapArray(Enum0[] enums) { + if (enums == null) return null; + Enum0Parcelable[] result = new Enum0Parcelable[enums.length]; + for (int i = 0; i < enums.length; i++) { + result[i] = new Enum0Parcelable(enums[i]); + } + return result; + } + + public static Enum0[] unwrapArray(Enum0Parcelable[] parcelables) { + if (parcelables == null) return null; + Enum0[] out = new Enum0[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getEnum0(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceMessageType.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceMessageType.java new file mode 100644 index 0000000..f58d6bd --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceMessageType.java @@ -0,0 +1,54 @@ +package testbed1.testbed1_android_messenger; + +public enum StructArray2InterfaceMessageType { + REGISTER_CLIENT(0), + UNREGISTER_CLIENT(1), + INIT(2), + PROP_PropBool(3), + SET_PropBool(4), + PROP_PropInt(5), + SET_PropInt(6), + PROP_PropFloat(7), + SET_PropFloat(8), + PROP_PropString(9), + SET_PropString(10), + PROP_PropEnum(11), + SET_PropEnum(12), + SIG_SigBool(13), + SIG_SigInt(14), + SIG_SigFloat(15), + SIG_SigString(16), + RPC_FuncBoolReq(17), + RPC_FuncBoolResp(18), + RPC_FuncIntReq(19), + RPC_FuncIntResp(20), + RPC_FuncFloatReq(21), + RPC_FuncFloatResp(22), + RPC_FuncStringReq(23), + RPC_FuncStringResp(24), + RPC_FuncEnumReq(25), + RPC_FuncEnumResp(26), + StructArray2InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); + + private final int value; + + StructArray2InterfaceMessageType(int value) {this.value = value;} + + public int getValue() + { + return this.value; + } + + public static StructArray2InterfaceMessageType fromInteger(int value) + { + for (StructArray2InterfaceMessageType event : StructArray2InterfaceMessageType.values()) + { + if (event.getValue() == value) + { + return event; + } + } + + return StructArray2InterfaceMessageType_UNKNOWN; + } +} diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceParcelable.java new file mode 100644 index 0000000..8743b37 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArray2InterfaceParcelable.java @@ -0,0 +1,87 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.IStructArray2Interface; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStringWithArray; + + public class StructArray2InterfaceParcelable implements Parcelable { + + public IStructArray2Interface data; + + public StructArray2InterfaceParcelable(IStructArray2Interface data) { + this.data = data; + } + + public IStructArray2Interface getStructArray2Interface() + { + return data; + } + + protected StructArray2InterfaceParcelable(Parcel in) { + StructBoolWithArrayParcelable l_parcelablepropBool = in.readParcelable(StructBoolWithArrayParcelable.class.getClassLoader(), StructBoolWithArrayParcelable.class); + data.setPropBool(l_parcelablepropBool != null ? l_parcelablepropBool.data : null); + StructIntWithArrayParcelable l_parcelablepropInt = in.readParcelable(StructIntWithArrayParcelable.class.getClassLoader(), StructIntWithArrayParcelable.class); + data.setPropInt(l_parcelablepropInt != null ? l_parcelablepropInt.data : null); + StructFloatWithArrayParcelable l_parcelablepropFloat = in.readParcelable(StructFloatWithArrayParcelable.class.getClassLoader(), StructFloatWithArrayParcelable.class); + data.setPropFloat(l_parcelablepropFloat != null ? l_parcelablepropFloat.data : null); + StructStringWithArrayParcelable l_parcelablepropString = in.readParcelable(StructStringWithArrayParcelable.class.getClassLoader(), StructStringWithArrayParcelable.class); + data.setPropString(l_parcelablepropString != null ? l_parcelablepropString.data : null); + StructEnumWithArrayParcelable l_parcelablepropEnum = in.readParcelable(StructEnumWithArrayParcelable.class.getClassLoader(), StructEnumWithArrayParcelable.class); + data.setPropEnum(l_parcelablepropEnum != null ? l_parcelablepropEnum.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructArray2InterfaceParcelable createFromParcel(Parcel in) { + return new StructArray2InterfaceParcelable(in); + } + + @Override + public StructArray2InterfaceParcelable[] newArray(int size) { + return new StructArray2InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new StructBoolWithArrayParcelable(data.getPropBool()), flags); + dest.writeParcelable(new StructIntWithArrayParcelable(data.getPropInt()), flags); + dest.writeParcelable(new StructFloatWithArrayParcelable(data.getPropFloat()), flags); + dest.writeParcelable(new StructStringWithArrayParcelable(data.getPropString()), flags); + dest.writeParcelable(new StructEnumWithArrayParcelable(data.getPropEnum()), flags); + + + } + public static StructArray2InterfaceParcelable[] wrapArray(IStructArray2Interface[] elements) { + if (elements == null) return null; + StructArray2InterfaceParcelable[] out = new StructArray2InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new StructArray2InterfaceParcelable(elements[i]); + } + return out; + } + + public static IStructArray2Interface[] unwrapArray(StructArray2InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IStructArray2Interface[] out = new IStructArray2Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructArray2Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java index 4be27cb..30844fb 100644 --- a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java @@ -12,18 +12,23 @@ public enum StructArrayInterfaceMessageType { SET_PropFloat(8), PROP_PropString(9), SET_PropString(10), - SIG_SigBool(11), - SIG_SigInt(12), - SIG_SigFloat(13), - SIG_SigString(14), - RPC_FuncBoolReq(15), - RPC_FuncBoolResp(16), - RPC_FuncIntReq(17), - RPC_FuncIntResp(18), - RPC_FuncFloatReq(19), - RPC_FuncFloatResp(20), - RPC_FuncStringReq(21), - RPC_FuncStringResp(22), + PROP_PropEnum(11), + SET_PropEnum(12), + SIG_SigBool(13), + SIG_SigInt(14), + SIG_SigFloat(15), + SIG_SigString(16), + SIG_SigEnum(17), + RPC_FuncBoolReq(18), + RPC_FuncBoolResp(19), + RPC_FuncIntReq(20), + RPC_FuncIntResp(21), + RPC_FuncFloatReq(22), + RPC_FuncFloatResp(23), + RPC_FuncStringReq(24), + RPC_FuncStringResp(25), + RPC_FuncEnumReq(26), + RPC_FuncEnumResp(27), StructArrayInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); private final int value; diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceParcelable.java new file mode 100644 index 0000000..fb3aa65 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceParcelable.java @@ -0,0 +1,82 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.IStructArrayInterface; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructString; + + public class StructArrayInterfaceParcelable implements Parcelable { + + public IStructArrayInterface data; + + public StructArrayInterfaceParcelable(IStructArrayInterface data) { + this.data = data; + } + + public IStructArrayInterface getStructArrayInterface() + { + return data; + } + + protected StructArrayInterfaceParcelable(Parcel in) { + StructBoolParcelable[] l_parcelablepropBool = in.createTypedArray(StructBoolParcelable.CREATOR); + data.setPropBool(StructBoolParcelable.unwrapArray(l_parcelablepropBool)); + StructIntParcelable[] l_parcelablepropInt = in.createTypedArray(StructIntParcelable.CREATOR); + data.setPropInt(StructIntParcelable.unwrapArray(l_parcelablepropInt)); + StructFloatParcelable[] l_parcelablepropFloat = in.createTypedArray(StructFloatParcelable.CREATOR); + data.setPropFloat(StructFloatParcelable.unwrapArray(l_parcelablepropFloat)); + StructStringParcelable[] l_parcelablepropString = in.createTypedArray(StructStringParcelable.CREATOR); + data.setPropString(StructStringParcelable.unwrapArray(l_parcelablepropString)); + Enum0Parcelable[] l_parcelablepropEnum = in.createTypedArray(Enum0Parcelable.CREATOR); + data.setPropEnum(Enum0Parcelable.unwrapArray(l_parcelablepropEnum)); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructArrayInterfaceParcelable createFromParcel(Parcel in) { + return new StructArrayInterfaceParcelable(in); + } + + @Override + public StructArrayInterfaceParcelable[] newArray(int size) { + return new StructArrayInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedArray(StructBoolParcelable.wrapArray(data.getPropBool()), flags); + dest.writeTypedArray(StructIntParcelable.wrapArray(data.getPropInt()), flags); + dest.writeTypedArray(StructFloatParcelable.wrapArray(data.getPropFloat()), flags); + dest.writeTypedArray(StructStringParcelable.wrapArray(data.getPropString()), flags); + dest.writeTypedArray(Enum0Parcelable.wrapArray(data.getPropEnum()), flags); + + + } + public static StructArrayInterfaceParcelable[] wrapArray(IStructArrayInterface[] elements) { + if (elements == null) return null; + StructArrayInterfaceParcelable[] out = new StructArrayInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new StructArrayInterfaceParcelable(elements[i]); + } + return out; + } + + public static IStructArrayInterface[] unwrapArray(StructArrayInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IStructArrayInterface[] out = new IStructArrayInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructArrayInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolWithArrayParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolWithArrayParcelable.java new file mode 100644 index 0000000..c91d2b7 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructBoolWithArrayParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructBoolWithArray; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructBoolWithArrayParcelable implements Parcelable { + + public StructBoolWithArray data; + + public StructBoolWithArrayParcelable(StructBoolWithArray data) { + this.data = new StructBoolWithArray(data); + } + + public StructBoolWithArray getStructBoolWithArray() + { + return new StructBoolWithArray(data); + } + + protected StructBoolWithArrayParcelable(Parcel in) { + this.data = new StructBoolWithArray(); + data.fieldBool = in.createBooleanArray(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructBoolWithArrayParcelable createFromParcel(Parcel in) { + return new StructBoolWithArrayParcelable(in); + } + + @Override + public StructBoolWithArrayParcelable[] newArray(int size) { + return new StructBoolWithArrayParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBooleanArray(data.fieldBool); + + + } + public static StructBoolWithArrayParcelable[] wrapArray(StructBoolWithArray[] structs) { + if (structs == null) return null; + StructBoolWithArrayParcelable[] out = new StructBoolWithArrayParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructBoolWithArrayParcelable(structs[i]); + } + return out; + } + + public static StructBoolWithArray[] unwrapArray(StructBoolWithArrayParcelable[] parcelables) { + if (parcelables == null) return null; + StructBoolWithArray[] out = new StructBoolWithArray[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructBoolWithArray(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumParcelable.java new file mode 100644 index 0000000..204cf96 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumParcelable.java @@ -0,0 +1,67 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructEnum; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.Enum0; + + public class StructEnumParcelable implements Parcelable { + + public StructEnum data; + + public StructEnumParcelable(StructEnum data) { + this.data = new StructEnum(data); + } + + public StructEnum getStructEnum() + { + return new StructEnum(data); + } + + protected StructEnumParcelable(Parcel in) { + this.data = new StructEnum(); + Enum0Parcelable l_parcelablefieldEnum = in.readParcelable(Enum0Parcelable.class.getClassLoader(), Enum0Parcelable.class); + data.fieldEnum = l_parcelablefieldEnum != null ? l_parcelablefieldEnum.data : null; + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructEnumParcelable createFromParcel(Parcel in) { + return new StructEnumParcelable(in); + } + + @Override + public StructEnumParcelable[] newArray(int size) { + return new StructEnumParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new Enum0Parcelable(data.fieldEnum), flags); + + + } + public static StructEnumParcelable[] wrapArray(StructEnum[] structs) { + if (structs == null) return null; + StructEnumParcelable[] out = new StructEnumParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructEnumParcelable(structs[i]); + } + return out; + } + + public static StructEnum[] unwrapArray(StructEnumParcelable[] parcelables) { + if (parcelables == null) return null; + StructEnum[] out = new StructEnum[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructEnum(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumWithArrayParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumWithArrayParcelable.java new file mode 100644 index 0000000..385c0cd --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructEnumWithArrayParcelable.java @@ -0,0 +1,67 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructEnumWithArray; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.Enum0; + + public class StructEnumWithArrayParcelable implements Parcelable { + + public StructEnumWithArray data; + + public StructEnumWithArrayParcelable(StructEnumWithArray data) { + this.data = new StructEnumWithArray(data); + } + + public StructEnumWithArray getStructEnumWithArray() + { + return new StructEnumWithArray(data); + } + + protected StructEnumWithArrayParcelable(Parcel in) { + this.data = new StructEnumWithArray(); + Enum0Parcelable[] l_parcelablefieldEnum = in.createTypedArray(Enum0Parcelable.CREATOR); + data.fieldEnum = Enum0Parcelable.unwrapArray(l_parcelablefieldEnum); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructEnumWithArrayParcelable createFromParcel(Parcel in) { + return new StructEnumWithArrayParcelable(in); + } + + @Override + public StructEnumWithArrayParcelable[] newArray(int size) { + return new StructEnumWithArrayParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedArray(Enum0Parcelable.wrapArray(data.fieldEnum), flags); + + + } + public static StructEnumWithArrayParcelable[] wrapArray(StructEnumWithArray[] structs) { + if (structs == null) return null; + StructEnumWithArrayParcelable[] out = new StructEnumWithArrayParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructEnumWithArrayParcelable(structs[i]); + } + return out; + } + + public static StructEnumWithArray[] unwrapArray(StructEnumWithArrayParcelable[] parcelables) { + if (parcelables == null) return null; + StructEnumWithArray[] out = new StructEnumWithArray[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructEnumWithArray(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatWithArrayParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatWithArrayParcelable.java new file mode 100644 index 0000000..2d61cac --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructFloatWithArrayParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructFloatWithArray; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructFloatWithArrayParcelable implements Parcelable { + + public StructFloatWithArray data; + + public StructFloatWithArrayParcelable(StructFloatWithArray data) { + this.data = new StructFloatWithArray(data); + } + + public StructFloatWithArray getStructFloatWithArray() + { + return new StructFloatWithArray(data); + } + + protected StructFloatWithArrayParcelable(Parcel in) { + this.data = new StructFloatWithArray(); + data.fieldFloat = in.createFloatArray(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructFloatWithArrayParcelable createFromParcel(Parcel in) { + return new StructFloatWithArrayParcelable(in); + } + + @Override + public StructFloatWithArrayParcelable[] newArray(int size) { + return new StructFloatWithArrayParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloatArray(data.fieldFloat); + + + } + public static StructFloatWithArrayParcelable[] wrapArray(StructFloatWithArray[] structs) { + if (structs == null) return null; + StructFloatWithArrayParcelable[] out = new StructFloatWithArrayParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructFloatWithArrayParcelable(structs[i]); + } + return out; + } + + public static StructFloatWithArray[] unwrapArray(StructFloatWithArrayParcelable[] parcelables) { + if (parcelables == null) return null; + StructFloatWithArray[] out = new StructFloatWithArray[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructFloatWithArray(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntWithArrayParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntWithArrayParcelable.java new file mode 100644 index 0000000..3af9dd1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructIntWithArrayParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructIntWithArray; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructIntWithArrayParcelable implements Parcelable { + + public StructIntWithArray data; + + public StructIntWithArrayParcelable(StructIntWithArray data) { + this.data = new StructIntWithArray(data); + } + + public StructIntWithArray getStructIntWithArray() + { + return new StructIntWithArray(data); + } + + protected StructIntWithArrayParcelable(Parcel in) { + this.data = new StructIntWithArray(); + data.fieldInt = in.createIntArray(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructIntWithArrayParcelable createFromParcel(Parcel in) { + return new StructIntWithArrayParcelable(in); + } + + @Override + public StructIntWithArrayParcelable[] newArray(int size) { + return new StructIntWithArrayParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(data.fieldInt); + + + } + public static StructIntWithArrayParcelable[] wrapArray(StructIntWithArray[] structs) { + if (structs == null) return null; + StructIntWithArrayParcelable[] out = new StructIntWithArrayParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructIntWithArrayParcelable(structs[i]); + } + return out; + } + + public static StructIntWithArray[] unwrapArray(StructIntWithArrayParcelable[] parcelables) { + if (parcelables == null) return null; + StructIntWithArray[] out = new StructIntWithArray[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructIntWithArray(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceParcelable.java new file mode 100644 index 0000000..1ba5db8 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructInterfaceParcelable.java @@ -0,0 +1,78 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.IStructInterface; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructString; + + public class StructInterfaceParcelable implements Parcelable { + + public IStructInterface data; + + public StructInterfaceParcelable(IStructInterface data) { + this.data = data; + } + + public IStructInterface getStructInterface() + { + return data; + } + + protected StructInterfaceParcelable(Parcel in) { + StructBoolParcelable l_parcelablepropBool = in.readParcelable(StructBoolParcelable.class.getClassLoader(), StructBoolParcelable.class); + data.setPropBool(l_parcelablepropBool != null ? l_parcelablepropBool.data : null); + StructIntParcelable l_parcelablepropInt = in.readParcelable(StructIntParcelable.class.getClassLoader(), StructIntParcelable.class); + data.setPropInt(l_parcelablepropInt != null ? l_parcelablepropInt.data : null); + StructFloatParcelable l_parcelablepropFloat = in.readParcelable(StructFloatParcelable.class.getClassLoader(), StructFloatParcelable.class); + data.setPropFloat(l_parcelablepropFloat != null ? l_parcelablepropFloat.data : null); + StructStringParcelable l_parcelablepropString = in.readParcelable(StructStringParcelable.class.getClassLoader(), StructStringParcelable.class); + data.setPropString(l_parcelablepropString != null ? l_parcelablepropString.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructInterfaceParcelable createFromParcel(Parcel in) { + return new StructInterfaceParcelable(in); + } + + @Override + public StructInterfaceParcelable[] newArray(int size) { + return new StructInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new StructBoolParcelable(data.getPropBool()), flags); + dest.writeParcelable(new StructIntParcelable(data.getPropInt()), flags); + dest.writeParcelable(new StructFloatParcelable(data.getPropFloat()), flags); + dest.writeParcelable(new StructStringParcelable(data.getPropString()), flags); + + + } + public static StructInterfaceParcelable[] wrapArray(IStructInterface[] elements) { + if (elements == null) return null; + StructInterfaceParcelable[] out = new StructInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new StructInterfaceParcelable(elements[i]); + } + return out; + } + + public static IStructInterface[] unwrapArray(StructInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IStructInterface[] out = new IStructInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringWithArrayParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringWithArrayParcelable.java new file mode 100644 index 0000000..be7c67c --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStringWithArrayParcelable.java @@ -0,0 +1,65 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructStringWithArray; +import android.os.Parcel; +import android.os.Parcelable; + + public class StructStringWithArrayParcelable implements Parcelable { + + public StructStringWithArray data; + + public StructStringWithArrayParcelable(StructStringWithArray data) { + this.data = new StructStringWithArray(data); + } + + public StructStringWithArray getStructStringWithArray() + { + return new StructStringWithArray(data); + } + + protected StructStringWithArrayParcelable(Parcel in) { + this.data = new StructStringWithArray(); + data.fieldString = in.createStringArray(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructStringWithArrayParcelable createFromParcel(Parcel in) { + return new StructStringWithArrayParcelable(in); + } + + @Override + public StructStringWithArrayParcelable[] newArray(int size) { + return new StructStringWithArrayParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStringArray(data.fieldString); + + + } + public static StructStringWithArrayParcelable[] wrapArray(StructStringWithArray[] structs) { + if (structs == null) return null; + StructStringWithArrayParcelable[] out = new StructStringWithArrayParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructStringWithArrayParcelable(structs[i]); + } + return out; + } + + public static StructStringWithArray[] unwrapArray(StructStringWithArrayParcelable[] parcelables) { + if (parcelables == null) return null; + StructStringWithArray[] out = new StructStringWithArray[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructStringWithArray(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructParcelable.java new file mode 100644 index 0000000..f3c8cec --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructParcelable.java @@ -0,0 +1,67 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructStruct; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.StructString; + + public class StructStructParcelable implements Parcelable { + + public StructStruct data; + + public StructStructParcelable(StructStruct data) { + this.data = new StructStruct(data); + } + + public StructStruct getStructStruct() + { + return new StructStruct(data); + } + + protected StructStructParcelable(Parcel in) { + this.data = new StructStruct(); + StructStringParcelable l_parcelablefieldString = in.readParcelable(StructStringParcelable.class.getClassLoader(), StructStringParcelable.class); + data.fieldString = l_parcelablefieldString != null ? l_parcelablefieldString.data : null; + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructStructParcelable createFromParcel(Parcel in) { + return new StructStructParcelable(in); + } + + @Override + public StructStructParcelable[] newArray(int size) { + return new StructStructParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new StructStringParcelable(data.fieldString), flags); + + + } + public static StructStructParcelable[] wrapArray(StructStruct[] structs) { + if (structs == null) return null; + StructStructParcelable[] out = new StructStructParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructStructParcelable(structs[i]); + } + return out; + } + + public static StructStruct[] unwrapArray(StructStructParcelable[] parcelables) { + if (parcelables == null) return null; + StructStruct[] out = new StructStruct[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructStruct(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructWithArrayParcelable.java b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructWithArrayParcelable.java new file mode 100644 index 0000000..5c948cd --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructStructWithArrayParcelable.java @@ -0,0 +1,67 @@ +package testbed1.testbed1_android_messenger; + +import testbed1.testbed1_api.StructStructWithArray; +import android.os.Parcel; +import android.os.Parcelable; +import testbed1.testbed1_api.StructStringWithArray; + + public class StructStructWithArrayParcelable implements Parcelable { + + public StructStructWithArray data; + + public StructStructWithArrayParcelable(StructStructWithArray data) { + this.data = new StructStructWithArray(data); + } + + public StructStructWithArray getStructStructWithArray() + { + return new StructStructWithArray(data); + } + + protected StructStructWithArrayParcelable(Parcel in) { + this.data = new StructStructWithArray(); + StructStringWithArrayParcelable[] l_parcelablefieldStruct = in.createTypedArray(StructStringWithArrayParcelable.CREATOR); + data.fieldStruct = StructStringWithArrayParcelable.unwrapArray(l_parcelablefieldStruct); + } + + public static final Creator CREATOR = new Creator() { + @Override + public StructStructWithArrayParcelable createFromParcel(Parcel in) { + return new StructStructWithArrayParcelable(in); + } + + @Override + public StructStructWithArrayParcelable[] newArray(int size) { + return new StructStructWithArrayParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedArray(StructStringWithArrayParcelable.wrapArray(data.fieldStruct), flags); + + + } + public static StructStructWithArrayParcelable[] wrapArray(StructStructWithArray[] structs) { + if (structs == null) return null; + StructStructWithArrayParcelable[] out = new StructStructWithArrayParcelable[structs.length]; + for (int i = 0; i < structs.length; i++) { + out[i] = new StructStructWithArrayParcelable(structs[i]); + } + return out; + } + + public static StructStructWithArray[] unwrapArray(StructStructWithArrayParcelable[] parcelables) { + if (parcelables == null) return null; + StructStructWithArray[] out = new StructStructWithArray[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getStructStructWithArray(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed1/testbed1_android_service/additions.gradle b/goldenmaster/testbed1/testbed1_android_service/additions.gradle index 19cf4ac..91b8304 100644 --- a/goldenmaster/testbed1/testbed1_android_service/additions.gradle +++ b/goldenmaster/testbed1/testbed1_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':testbed1_api') - implementation project(':testbed1_impl') - implementation project(':testbed1_android_messenger') + api project(':testbed1_impl') + api project(':testbed1_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/testbed1/testbed1_android_service/build.gradle b/goldenmaster/testbed1/testbed1_android_service/build.gradle index a7f6d9e..5d590f1 100644 --- a/goldenmaster/testbed1/testbed1_android_service/build.gradle +++ b/goldenmaster/testbed1/testbed1_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "testbed1" version = "1.0.0" - android { namespace 'testbed1.testbed1_android_service' compileSdk 35 diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml b/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml index bfd5dda..4f90ed4 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml @@ -18,6 +18,10 @@ android:name="testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter" android:enabled="true" android:exported="true"> + \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArray2InterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArray2InterfaceServiceFactory.java new file mode 100644 index 0000000..d5587b7 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/IStructArray2InterfaceServiceFactory.java @@ -0,0 +1,7 @@ +package testbed1.testbed1_android_service; +import testbed1.testbed1_api.IStructArray2Interface; + + +public interface IStructArray2InterfaceServiceFactory { + public IStructArray2Interface getServiceInstance(); +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java new file mode 100644 index 0000000..9f4ed49 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java @@ -0,0 +1,587 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_android_service.IStructArray2InterfaceServiceFactory; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceMessageType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class StructArray2InterfaceServiceAdapter extends Service +{ + private static final String TAG = "StructArray2InterfaceServiceAdapter"; + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + private static Messenger mMessenger; + private static IncomingHandler mHandler = null; + private static IStructArray2Interface mBackendService; + private static IStructArray2InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); + + //private final List mMessagesQueue = new ArrayList<>(); + + public StructArray2InterfaceServiceAdapter() + { + } + + public static IStructArray2Interface setService(IStructArray2InterfaceServiceFactory factory) + { + Log.i(TAG, "Setting factory: " + factory); + if (mServiceFactory != factory) + { + mServiceFactory = factory; + } + synchronized (mutex) + { + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(StructArray2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(StructArray2InterfaceService) called. context = " + this); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + + } + + // execution of service will start on calling this method + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Log.i(TAG, "LIFECYCLE: StructArray2InterfaceService::onStartCommand called. context = " + this + + ", startID=" + startId); + + return START_STICKY; + } + + // execution of the service will stop on calling this method + @Override + public void onDestroy() + { + Log.i(TAG, "LIFECYCLE: onDestroy(StructArray2InterfaceService) - proc = " + ", mMessenger = " + mMessenger); + + if (mHandler != null) + { + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onBind(intent) - proc=" + ", intent=" + intent); + + //Log.i(TAG, "binding attachId=" + attachId); + return mMessenger.getBinder(); + } + + + @Override + public boolean onUnbind(Intent intent) + { + Log.i(TAG, "LIFECYCLE: onUnbind(intent) - proc=" + ", mMessenger=" + mMessenger + + ", intent=" + intent); + + return super.onUnbind(intent); + } + + private static String Name(Context context) + { + return context.getPackageName() + ",context=" + context; + } + //TODO Listener for handling messanger + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler implements IStructArray2InterfaceEventListener + { + private final Service mApplicationContext; + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler(Service context) + { + super(Looper.getMainLooper()); + mApplicationContext = context; + } + + private void sendMessageToClients(Message msg) + { + for (Map.Entry client : mClients.entrySet()) + { + Messenger reply = client.getValue(); + if (reply != null) + { + try + { + reply.send(msg); + } catch (RemoteException e) + { + Log.e(TAG, "Can't send reply " + e); + } + } + } + } + + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + if (mBackendService == null || !mBackendService._isReady()) + { + if (StructArray2InterfaceMessageType.fromInteger(msg.what) != StructArray2InterfaceMessageType.REGISTER_CLIENT + && StructArray2InterfaceMessageType.fromInteger(msg.what) != StructArray2InterfaceMessageType.UNREGISTER_CLIENT) + { + Log.w(TAG, "Check if server is ready, messsage will be dropped. MsgType: StructArray2InterfaceMessageType" + StructArray2InterfaceMessageType.fromInteger(msg.what) ); + return; + } + } + switch (StructArray2InterfaceMessageType.fromInteger(msg.what)) + { + case REGISTER_CLIENT: + addClientActivity(msg.replyTo, msg.getData().getString("connectionID", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + case PROP_PropBool: + { + Bundle data = msg.getData(); + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + StructBoolWithArray propBool = data.getParcelable("propBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + mBackendService.setPropBool(propBool); + break; + } + case PROP_PropInt: + { + Bundle data = msg.getData(); + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + + StructIntWithArray propInt = data.getParcelable("propInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + mBackendService.setPropInt(propInt); + break; + } + case PROP_PropFloat: + { + Bundle data = msg.getData(); + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + + StructFloatWithArray propFloat = data.getParcelable("propFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + mBackendService.setPropFloat(propFloat); + break; + } + case PROP_PropString: + { + Bundle data = msg.getData(); + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + + StructStringWithArray propString = data.getParcelable("propString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + mBackendService.setPropString(propString); + break; + } + case PROP_PropEnum: + { + Bundle data = msg.getData(); + data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + + StructEnumWithArray propEnum = data.getParcelable("propEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + mBackendService.setPropEnum(propEnum); + break; + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncBoolReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructBoolWithArray paramBool = data.getParcelable("paramBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + + StructBool[] result = mBackendService.funcBool(paramBool); + + Message respMsg = new Message(); + respMsg.what = StructArray2InterfaceMessageType.RPC_FuncBoolResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructBoolParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncIntReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructIntWithArray paramInt = data.getParcelable("paramInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + + StructInt[] result = mBackendService.funcInt(paramInt); + + Message respMsg = new Message(); + respMsg.what = StructArray2InterfaceMessageType.RPC_FuncIntResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructIntParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncFloatReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructFloatWithArray paramFloat = data.getParcelable("paramFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + + StructFloat[] result = mBackendService.funcFloat(paramFloat); + + Message respMsg = new Message(); + respMsg.what = StructArray2InterfaceMessageType.RPC_FuncFloatResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructFloatParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncStringReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructStringWithArray paramString = data.getParcelable("paramString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + + StructString[] result = mBackendService.funcString(paramString); + + Message respMsg = new Message(); + respMsg.what = StructArray2InterfaceMessageType.RPC_FuncStringResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",StructStringParcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncEnumReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + StructEnumWithArray paramEnum = data.getParcelable("paramEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + + Enum0[] result = mBackendService.funcEnum(paramEnum); + + Message respMsg = new Message(); + respMsg.what = StructArray2InterfaceMessageType.RPC_FuncEnumResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",Enum0Parcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + default: + Log.e(TAG, "Receive Unsupported message: " + msg.what); + super.handleMessage(msg); + break; + } + } + + @Override + protected void finalize() throws Throwable + { + super.finalize(); + Log.i(TAG, "LIFECYCLE: IncomingHandler(finalize)"); + } + + private void addClientActivity(Messenger serviceReply, String connectionID) + { + if (serviceReply != null) + { + mClients.put(connectionID, serviceReply); + Log.i(TAG, "Register event listener with connectionID = " + connectionID); + } + } + + private void removeClientActivity(String connectionID) + { + mClients.remove(connectionID); + Log.i(TAG, "UnRegister event listener with connectionID = " + connectionID); + } + + private void sendInit() + { + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.INIT.getValue(); + Bundle data = new Bundle(); + + StructBoolWithArray propBool = mBackendService.getPropBool(); + + data.putParcelable("propBool", new StructBoolWithArrayParcelable(propBool)); + StructIntWithArray propInt = mBackendService.getPropInt(); + + data.putParcelable("propInt", new StructIntWithArrayParcelable(propInt)); + StructFloatWithArray propFloat = mBackendService.getPropFloat(); + + data.putParcelable("propFloat", new StructFloatWithArrayParcelable(propFloat)); + StructStringWithArray propString = mBackendService.getPropString(); + + data.putParcelable("propString", new StructStringWithArrayParcelable(propString)); + StructEnumWithArray propEnum = mBackendService.getPropEnum(); + + data.putParcelable("propEnum", new StructEnumWithArrayParcelable(propEnum)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropBoolChanged(StructBoolWithArray propBool){ + Log.i(TAG, "New value for PropBool from backend" + propBool); + + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SET_PropBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propBool", new StructBoolWithArrayParcelable(propBool)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropIntChanged(StructIntWithArray propInt){ + Log.i(TAG, "New value for PropInt from backend" + propInt); + + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SET_PropInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propInt", new StructIntWithArrayParcelable(propInt)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropFloatChanged(StructFloatWithArray propFloat){ + Log.i(TAG, "New value for PropFloat from backend" + propFloat); + + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SET_PropFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propFloat", new StructFloatWithArrayParcelable(propFloat)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropStringChanged(StructStringWithArray propString){ + Log.i(TAG, "New value for PropString from backend" + propString); + + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SET_PropString.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propString", new StructStringWithArrayParcelable(propString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onPropEnumChanged(StructEnumWithArray propEnum){ + Log.i(TAG, "New value for PropEnum from backend" + propEnum); + + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SET_PropEnum.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("propEnum", new StructEnumWithArrayParcelable(propEnum)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigBool(StructBoolWithArray paramBool){ + Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SIG_SigBool.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramBool", new StructBoolWithArrayParcelable(paramBool)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigInt(StructIntWithArray paramInt){ + Log.i(TAG, "New singal for SigInt = "+ " " + paramInt); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SIG_SigInt.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramInt", new StructIntWithArrayParcelable(paramInt)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigFloat(StructFloatWithArray paramFloat){ + Log.i(TAG, "New singal for SigFloat = "+ " " + paramFloat); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SIG_SigFloat.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramFloat", new StructFloatWithArrayParcelable(paramFloat)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void onSigString(StructStringWithArray paramString){ + Log.i(TAG, "New singal for SigString = "+ " " + paramString); + Message msg = new Message(); + msg.what = StructArray2InterfaceMessageType.SIG_SigString.getValue(); + Bundle data = new Bundle(); + + data.putParcelable("paramString", new StructStringWithArrayParcelable(paramString)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override + public void on_readyStatusChanged(boolean isReady) { + if (isReady){ + Log.i(TAG, "Backend ready "); + } + else { + Log.i(TAG, "Backend not ready "); + } + } + } +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java new file mode 100644 index 0000000..b878e01 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import testbed1.testbed1_android_service.IStructArray2InterfaceServiceFactory; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_impl.StructArray2InterfaceService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton StructArray2InterfaceServiceFactory thread for the system. This is a thread for + * StructArray2InterfaceServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class StructArray2InterfaceServiceFactory extends HandlerThread implements IStructArray2InterfaceServiceFactory +{ + private StructArray2InterfaceService m_Service; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static StructArray2InterfaceServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: StructArray2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractStructArray2Interface getServiceInstance() + { + if (m_Service == null) + { + m_Service = new StructArray2InterfaceService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new StructArray2InterfaceService"); + } + + return m_Service; + } + + private static class Singleton + { + private static final StructArray2InterfaceServiceFactory INSTANCE = createInstance(); + } + + private StructArray2InterfaceServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static StructArray2InterfaceServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + StructArray2InterfaceServiceFactory t = new StructArray2InterfaceServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java new file mode 100644 index 0000000..7db17af --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java @@ -0,0 +1,42 @@ +package testbed1.testbed1_android_service; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter; +import testbed1.testbed1_android_service.StructArray2InterfaceServiceFactory; + + +//Use this class to manage lifetime of android server with Implemented backend service from package testbed1.testbed1_impl; . +public class StructArray2InterfaceServiceStarter { + + static Intent androidService = null; + private static final String TAG = "StructArray2InterfaceStarter"; + + + + public static IStructArray2Interface start(Context context) { + stop(context); + androidService = new Intent(context, StructArray2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + StructArray2InterfaceServiceFactory factory = StructArray2InterfaceServiceFactory.get(); + Log.w(TAG, "starter: factory set for StructArray2InterfaceServiceFactory"); + return StructArray2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java index 8f2bfae..8d56507 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java @@ -14,12 +14,14 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.StructBool; import testbed1.testbed1_android_messenger.StructBoolParcelable; -import testbed1.testbed1_api.StructInt; -import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; @@ -37,10 +39,11 @@ public class StructArrayInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static IStructArrayInterface mBackendService; private static IStructArrayInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +58,19 @@ public static IStructArrayInterface setService(IStructArrayInterfaceServiceFacto { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(StructArrayInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +81,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(StructArrayInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +116,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(StructArrayInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(StructArrayInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(StructArrayInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -221,13 +248,23 @@ public void handleMessage(Message msg) mBackendService.setPropString(propString); break; } + case PROP_PropEnum: + { + Bundle data = msg.getData(); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0[] propEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + mBackendService.setPropEnum(propEnum); + break; + } // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. case RPC_FuncBoolReq: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructBool[] paramBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); @@ -256,7 +293,8 @@ public void handleMessage(Message msg) case RPC_FuncIntReq: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructInt[] paramInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); @@ -285,7 +323,8 @@ public void handleMessage(Message msg) case RPC_FuncFloatReq: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructFloat[] paramFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); @@ -314,7 +353,8 @@ public void handleMessage(Message msg) case RPC_FuncStringReq: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructString[] paramString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); @@ -337,6 +377,36 @@ public void handleMessage(Message msg) break; } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncEnumReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Enum0[] paramEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("paramEnum", Enum0Parcelable.class)); + + Enum0[] result = mBackendService.funcEnum(paramEnum); + + Message respMsg = new Message(); + respMsg.what = StructArrayInterfaceMessageType.RPC_FuncEnumResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelableArray("result",Enum0Parcelable.wrapArray(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } default: Log.e(TAG, "Receive Unsupported message: " + msg.what); super.handleMessage(msg); @@ -384,6 +454,9 @@ private void sendInit() StructString[] propString = mBackendService.getPropString(); data.putParcelableArray("propString", StructStringParcelable.wrapArray(propString)); + Enum0[] propEnum = mBackendService.getPropEnum(); + + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(propEnum)); msg.setData(data); sendMessageToClients(msg); } @@ -436,6 +509,18 @@ public void onPropStringChanged(StructString[] propString){ sendMessageToClients(msg); } @Override + public void onPropEnumChanged(Enum0[] propEnum){ + Log.i(TAG, "New value for PropEnum from backend" + propEnum); + + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SET_PropEnum.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(propEnum)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override public void onSigBool(StructBool[] paramBool){ Log.i(TAG, "New singal for SigBool = "+ " " + paramBool); Message msg = new Message(); @@ -480,6 +565,17 @@ public void onSigString(StructString[] paramString){ sendMessageToClients(msg); } @Override + public void onSigEnum(Enum0[] paramEnum){ + Log.i(TAG, "New singal for SigEnum = "+ " " + paramEnum); + Message msg = new Message(); + msg.what = StructArrayInterfaceMessageType.SIG_SigEnum.getValue(); + Bundle data = new Bundle(); + + data.putParcelableArray("paramEnum", Enum0Parcelable.wrapArray(paramEnum)); + msg.setData(data); + sendMessageToClients(msg); + } + @Override public void on_readyStatusChanged(boolean isReady) { if (isReady){ Log.i(TAG, "Backend ready "); diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java index 1bd3a73..ccd4507 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java @@ -16,10 +16,10 @@ import android.util.Log; import testbed1.testbed1_api.StructBool; import testbed1.testbed1_android_messenger.StructBoolParcelable; -import testbed1.testbed1_api.StructInt; -import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; @@ -37,10 +37,11 @@ public class StructInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static IStructInterface mBackendService; private static IStructInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -55,10 +56,19 @@ public static IStructInterface setService(IStructInterfaceServiceFactory factory { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(StructInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -69,13 +79,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(StructInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -92,18 +114,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(StructInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(StructInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(StructInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -227,7 +252,8 @@ public void handleMessage(Message msg) case RPC_FuncBoolReq: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructBool paramBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); @@ -256,7 +282,8 @@ public void handleMessage(Message msg) case RPC_FuncIntReq: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructInt paramInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); @@ -285,7 +312,8 @@ public void handleMessage(Message msg) case RPC_FuncFloatReq: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructFloat paramFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); @@ -314,7 +342,8 @@ public void handleMessage(Message msg) case RPC_FuncStringReq: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); StructString paramString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..a420164 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java @@ -0,0 +1,677 @@ +//TODO later// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1_android_service; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter; + +//import message type and parcelabe types +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_android_messenger.StructStructParcelable; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_android_messenger.StructEnumParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_android_messenger.StructStructWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; +import testbed1.testbed1_api.Testbed1TestHelper; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; + + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_android_service.IStructArray2InterfaceServiceFactory; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceMessageType; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.InOrder; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ServiceController; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +interface IStructArray2InterfaceMessageGetter +{ + public void getMessage(Message msg); +} + +@Config(sdk = 33, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class StructArray2InterfaceServiceAdapterTest +{ + + @Mock + private Context mMockContext; + + private StructArray2InterfaceServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private IStructArray2InterfaceEventListener testedAdapterAsEventListener; + private Messenger mServiceMessenger; + private IStructArray2Interface backendServiceMock = mock(IStructArray2Interface.class); + InOrder inOrderBackendService; + InOrder inOrderClientMessagesHandler; + + private Handler clientReplyHandler ; + private Messenger clientReplyMessenger; + private String mTestConnectionID1 = "MyTestClient"; + + private IStructArray2InterfaceServiceFactory serviceFactory = mock(IStructArray2InterfaceServiceFactory.class); + private IStructArray2InterfaceMessageGetter clientMessagesStorage = mock(IStructArray2InterfaceMessageGetter.class); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + @After + public void tearDown() { + + Message unregisterMsg = Message.obtain(null, StructArray2InterfaceMessageType.UNREGISTER_CLIENT.ordinal()); + unregisterMsg.getData().putString("connectionID", mTestConnectionID1); + + try { + mServiceMessenger.send(unregisterMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + if (mMockContext != null) { + mMockContext.stopService(testedServiceAdapterIntent); + } + testedServiceAdapter.onDestroy(); + inOrderBackendService.verify(backendServiceMock, times(1)).removeEventListener(testedAdapterAsEventListener); + } + + Handler createClientHandlerMock(IStructArray2InterfaceMessageGetter messageGetterMock) + { + return new Handler(Looper.getMainLooper()){ + @Override + public void handleMessage(Message msg) { + Message copy = Message.obtain(); + copy.copyFrom(msg); + messageGetterMock.getMessage(copy); + } + }; + } + + void registerFakeActivityClient(Messenger messenger, String id) + { + StructBoolWithArray initpropBool = new StructBoolWithArray(); + //TODO fill fields + when(backendServiceMock.getPropBool()).thenReturn(initpropBool); + StructIntWithArray initpropInt = new StructIntWithArray(); + //TODO fill fields + when(backendServiceMock.getPropInt()).thenReturn(initpropInt); + StructFloatWithArray initpropFloat = new StructFloatWithArray(); + //TODO fill fields + when(backendServiceMock.getPropFloat()).thenReturn(initpropFloat); + StructStringWithArray initpropString = new StructStringWithArray(); + //TODO fill fields + when(backendServiceMock.getPropString()).thenReturn(initpropString); + StructEnumWithArray initpropEnum = new StructEnumWithArray(); + //TODO fill fields + when(backendServiceMock.getPropEnum()).thenReturn(initpropEnum); + + + Message registerMsg = Message.obtain(null, StructArray2InterfaceMessageType.REGISTER_CLIENT.ordinal()); + registerMsg.getData().putString("connectionID", id); + registerMsg.replyTo = messenger; + + try { + mServiceMessenger.send(registerMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropBool(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropString(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropEnum(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.INIT.getValue(), response.what); + Bundle data = response.getData(); + + StructBoolWithArray receivedpropBool = data.getParcelable("propBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + + StructIntWithArray receivedpropInt = data.getParcelable("propInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + + StructFloatWithArray receivedpropFloat = data.getParcelable("propFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + + StructStringWithArray receivedpropString = data.getParcelable("propString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + + StructEnumWithArray receivedpropEnum = data.getParcelable("propEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + // assertEquals(receivedpropBool, initpropBool); + // assertEquals(receivedpropInt, initpropInt); + // assertEquals(receivedpropFloat, initpropFloat); + // assertEquals(receivedpropString, initpropString); + // assertEquals(receivedpropEnum, initpropEnum); + + } + + @Before + public void setUp() throws RemoteException + { + clientReplyHandler = createClientHandlerMock(clientMessagesStorage); + clientReplyMessenger = new Messenger(clientReplyHandler); + inOrderClientMessagesHandler = inOrder(clientMessagesStorage); + inOrderBackendService = inOrder(backendServiceMock); + mMockContext = RuntimeEnvironment.getApplication(); + + when(backendServiceMock._isReady()).thenReturn(true); + + testedServiceAdapterIntent = new Intent(mMockContext, StructArray2InterfaceServiceAdapter.class); + + // Start the service + testedServiceAdapter = Robolectric.buildService(StructArray2InterfaceServiceAdapter.class, testedServiceAdapterIntent) + .create().get(); + // Assert expected state + assertNotNull(testedServiceAdapter); + + // Get Messenger instance from service + IBinder binder = (IBinder)testedServiceAdapter.onBind(new Intent()) ; + mServiceMessenger = new Messenger(binder); + + when(serviceFactory.getServiceInstance()).thenReturn(backendServiceMock); + testedServiceAdapter.setService(serviceFactory); + // service adapter should pass its member to backend to get notifications on changes. + ArgumentCaptor eventListnerCaptor = ArgumentCaptor.forClass(IStructArray2InterfaceEventListener.class); + inOrderBackendService.verify(backendServiceMock, times(1)).addEventListener(eventListnerCaptor.capture()); + testedAdapterAsEventListener = eventListnerCaptor.getValue(); + + // Register a fake client (the message handler form its service-connection) + // All emitted signals and property changes are forwarded to it. + registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); + } + @Test + public void onReceivepropBoolPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.PROP_PropBool.getValue()); + Bundle data = new Bundle(); + StructBoolWithArray testpropBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + data.putParcelable("propBool", new StructBoolWithArrayParcelable(testpropBool)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropBool( any(StructBoolWithArray.class)); + + } + + @Test + public void whenNotifiedpropBool() + { + StructBoolWithArray testpropBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + + testedAdapterAsEventListener.onPropBoolChanged(testpropBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SET_PropBool.getValue(), response.what); + Bundle data = response.getData(); + + + StructBoolWithArray receivedpropBool = data.getParcelable("propBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + + assertEquals(receivedpropBool, testpropBool); + } + @Test + public void onReceivepropIntPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.PROP_PropInt.getValue()); + Bundle data = new Bundle(); + StructIntWithArray testpropInt = Testbed1TestHelper.makeTestStructIntWithArray(); + data.putParcelable("propInt", new StructIntWithArrayParcelable(testpropInt)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropInt( any(StructIntWithArray.class)); + + } + + @Test + public void whenNotifiedpropInt() + { + StructIntWithArray testpropInt = Testbed1TestHelper.makeTestStructIntWithArray(); + + testedAdapterAsEventListener.onPropIntChanged(testpropInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SET_PropInt.getValue(), response.what); + Bundle data = response.getData(); + + + StructIntWithArray receivedpropInt = data.getParcelable("propInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + + assertEquals(receivedpropInt, testpropInt); + } + @Test + public void onReceivepropFloatPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.PROP_PropFloat.getValue()); + Bundle data = new Bundle(); + StructFloatWithArray testpropFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + data.putParcelable("propFloat", new StructFloatWithArrayParcelable(testpropFloat)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropFloat( any(StructFloatWithArray.class)); + + } + + @Test + public void whenNotifiedpropFloat() + { + StructFloatWithArray testpropFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + + testedAdapterAsEventListener.onPropFloatChanged(testpropFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SET_PropFloat.getValue(), response.what); + Bundle data = response.getData(); + + + StructFloatWithArray receivedpropFloat = data.getParcelable("propFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + + assertEquals(receivedpropFloat, testpropFloat); + } + @Test + public void onReceivepropStringPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.PROP_PropString.getValue()); + Bundle data = new Bundle(); + StructStringWithArray testpropString = Testbed1TestHelper.makeTestStructStringWithArray(); + data.putParcelable("propString", new StructStringWithArrayParcelable(testpropString)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropString( any(StructStringWithArray.class)); + + } + + @Test + public void whenNotifiedpropString() + { + StructStringWithArray testpropString = Testbed1TestHelper.makeTestStructStringWithArray(); + + testedAdapterAsEventListener.onPropStringChanged(testpropString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SET_PropString.getValue(), response.what); + Bundle data = response.getData(); + + + StructStringWithArray receivedpropString = data.getParcelable("propString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + + assertEquals(receivedpropString, testpropString); + } + @Test + public void onReceivepropEnumPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.PROP_PropEnum.getValue()); + Bundle data = new Bundle(); + StructEnumWithArray testpropEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + data.putParcelable("propEnum", new StructEnumWithArrayParcelable(testpropEnum)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropEnum( any(StructEnumWithArray.class)); + + } + + @Test + public void whenNotifiedpropEnum() + { + StructEnumWithArray testpropEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + + testedAdapterAsEventListener.onPropEnumChanged(testpropEnum); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SET_PropEnum.getValue(), response.what); + Bundle data = response.getData(); + + + StructEnumWithArray receivedpropEnum = data.getParcelable("propEnum", StructEnumWithArrayParcelable.class).getStructEnumWithArray(); + + assertEquals(receivedpropEnum, testpropEnum); + } + @Test + public void whenNotifiedsigBool() + { + StructBoolWithArray testparamBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + + testedAdapterAsEventListener.onSigBool(testparamBool); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SIG_SigBool.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + + StructBoolWithArray receivedparamBool = data.getParcelable("paramBool", StructBoolWithArrayParcelable.class).getStructBoolWithArray(); + assertEquals(receivedparamBool, testparamBool); +} + @Test + public void whenNotifiedsigInt() + { + StructIntWithArray testparamInt = Testbed1TestHelper.makeTestStructIntWithArray(); + + testedAdapterAsEventListener.onSigInt(testparamInt); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SIG_SigInt.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + + StructIntWithArray receivedparamInt = data.getParcelable("paramInt", StructIntWithArrayParcelable.class).getStructIntWithArray(); + assertEquals(receivedparamInt, testparamInt); +} + @Test + public void whenNotifiedsigFloat() + { + StructFloatWithArray testparamFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + + testedAdapterAsEventListener.onSigFloat(testparamFloat); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SIG_SigFloat.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + + StructFloatWithArray receivedparamFloat = data.getParcelable("paramFloat", StructFloatWithArrayParcelable.class).getStructFloatWithArray(); + assertEquals(receivedparamFloat, testparamFloat); +} + @Test + public void whenNotifiedsigString() + { + StructStringWithArray testparamString = Testbed1TestHelper.makeTestStructStringWithArray(); + + testedAdapterAsEventListener.onSigString(testparamString); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.SIG_SigString.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + + StructStringWithArray receivedparamString = data.getParcelable("paramString", StructStringWithArrayParcelable.class).getStructStringWithArray(); + assertEquals(receivedparamString, testparamString); +} + + + public void onfuncBoolRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncBoolReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructBoolWithArray testparamBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + data.putParcelable("paramBool", new StructBoolWithArrayParcelable(testparamBool)); + StructBool[] returnedValue = new StructBool[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructBool(); + + + when(backendServiceMock.funcBool( any(StructBoolWithArray.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcBool( any(StructBoolWithArray.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.RPC_FuncBoolResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + StructBool[] receivedByClient = StructBoolParcelable.unwrapArray((StructBoolParcelable[])resp_data.getParcelableArray("result", StructBoolParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncIntRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncIntReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructIntWithArray testparamInt = Testbed1TestHelper.makeTestStructIntWithArray(); + data.putParcelable("paramInt", new StructIntWithArrayParcelable(testparamInt)); + StructInt[] returnedValue = new StructInt[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructInt(); + + + when(backendServiceMock.funcInt( any(StructIntWithArray.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcInt( any(StructIntWithArray.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.RPC_FuncIntResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructIntParcelable.class.getClassLoader()); + StructInt[] receivedByClient = StructIntParcelable.unwrapArray((StructIntParcelable[])resp_data.getParcelableArray("result", StructIntParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncFloatRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncFloatReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructFloatWithArray testparamFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + data.putParcelable("paramFloat", new StructFloatWithArrayParcelable(testparamFloat)); + StructFloat[] returnedValue = new StructFloat[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructFloat(); + + + when(backendServiceMock.funcFloat( any(StructFloatWithArray.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcFloat( any(StructFloatWithArray.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.RPC_FuncFloatResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + StructFloat[] receivedByClient = StructFloatParcelable.unwrapArray((StructFloatParcelable[])resp_data.getParcelableArray("result", StructFloatParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncStringRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncStringReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructStringWithArray testparamString = Testbed1TestHelper.makeTestStructStringWithArray(); + data.putParcelable("paramString", new StructStringWithArrayParcelable(testparamString)); + StructString[] returnedValue = new StructString[1]; + returnedValue[0] = Testbed1TestHelper.makeTestStructString(); + + + when(backendServiceMock.funcString( any(StructStringWithArray.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcString( any(StructStringWithArray.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.RPC_FuncStringResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(StructStringParcelable.class.getClassLoader()); + StructString[] receivedByClient = StructStringParcelable.unwrapArray((StructStringParcelable[])resp_data.getParcelableArray("result", StructStringParcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncEnumRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArray2InterfaceMessageType.RPC_FuncEnumReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + StructEnumWithArray testparamEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + data.putParcelable("paramEnum", new StructEnumWithArrayParcelable(testparamEnum)); + Enum0[] returnedValue = new Enum0[1]; + returnedValue[0] = Enum0.Value1; + + + when(backendServiceMock.funcEnum( any(StructEnumWithArray.class))).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcEnum( any(StructEnumWithArray.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArray2InterfaceMessageType.RPC_FuncEnumResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + Enum0[] receivedByClient = Enum0Parcelable.unwrapArray((Enum0Parcelable[])resp_data.getParcelableArray("result", Enum0Parcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + +} diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java index 6167db6..1b8e366 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java @@ -25,7 +25,25 @@ import testbed1.testbed1_android_messenger.StructFloatParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_android_messenger.StructStructParcelable; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_android_messenger.StructEnumParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_android_messenger.StructStructWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; import testbed1.testbed1_api.Testbed1TestHelper; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.IStructArrayInterfaceEventListener; @@ -158,6 +176,10 @@ void registerFakeActivityClient(Messenger messenger, String id) // todo fill if is struct StructString[] initpropString = new StructString[]{ init_elementpropString } ; when(backendServiceMock.getPropString()).thenReturn(initpropString); + Enum0 init_elementpropEnum = Enum0.Value1; + // todo fill if is struct + Enum0[] initpropEnum = new Enum0[]{ init_elementpropEnum } ; + when(backendServiceMock.getPropEnum()).thenReturn(initpropEnum); Message registerMsg = Message.obtain(null, StructArrayInterfaceMessageType.REGISTER_CLIENT.ordinal()); @@ -174,6 +196,7 @@ void registerFakeActivityClient(Messenger messenger, String id) inOrderBackendService.verify(backendServiceMock, times(1)).getPropInt(); inOrderBackendService.verify(backendServiceMock, times(1)).getPropFloat(); inOrderBackendService.verify(backendServiceMock, times(1)).getPropString(); + inOrderBackendService.verify(backendServiceMock, times(1)).getPropEnum(); inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -188,14 +211,15 @@ void registerFakeActivityClient(Messenger messenger, String id) StructFloat[] receivedpropFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("propFloat", StructFloatParcelable.class)); StructString[] receivedpropString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("propString", StructStringParcelable.class)); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + Enum0[] receivedpropEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); // assertEquals(receivedpropBool, initpropBool); // assertEquals(receivedpropInt, initpropInt); // assertEquals(receivedpropFloat, initpropFloat); // assertEquals(receivedpropString, initpropString); + assertEquals(receivedpropEnum, initpropEnum); } @@ -233,7 +257,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -270,7 +293,6 @@ public void whenNotifiedpropBool() assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -307,7 +329,6 @@ public void whenNotifiedpropInt() assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -344,7 +365,6 @@ public void whenNotifiedpropFloat() assertEquals(receivedpropFloat, testpropFloat); } -//TODO do not add when a property is readonly @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -382,6 +402,42 @@ public void whenNotifiedpropString() assertEquals(receivedpropString, testpropString); } @Test + public void onReceivepropEnumPropertyChangeTest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.PROP_PropEnum.getValue()); + Bundle data = new Bundle(); + Enum0[] testpropEnum = new Enum0[1]; + testpropEnum[0] = Enum0.Value1; + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(testpropEnum)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).setPropEnum(testpropEnum); + + } + + @Test + public void whenNotifiedpropEnum() + { + Enum0[] testpropEnum = new Enum0[1]; + testpropEnum[0] = Enum0.Value1; + + testedAdapterAsEventListener.onPropEnumChanged(testpropEnum); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SET_PropEnum.getValue(), response.what); + Bundle data = response.getData(); + + + Enum0[] receivedpropEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + + assertEquals(receivedpropEnum, testpropEnum); + } + @Test public void whenNotifiedsigBool() { StructBool[] testparamBool = new StructBool[1]; @@ -395,7 +451,8 @@ public void whenNotifiedsigBool() assertEquals(StructArrayInterfaceMessageType.SIG_SigBool.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool[] receivedparamBool = StructBoolParcelable.unwrapArray((StructBoolParcelable[])data.getParcelableArray("paramBool", StructBoolParcelable.class)); assertEquals(receivedparamBool, testparamBool); @@ -414,7 +471,8 @@ public void whenNotifiedsigInt() assertEquals(StructArrayInterfaceMessageType.SIG_SigInt.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); StructInt[] receivedparamInt = StructIntParcelable.unwrapArray((StructIntParcelable[])data.getParcelableArray("paramInt", StructIntParcelable.class)); assertEquals(receivedparamInt, testparamInt); @@ -433,7 +491,8 @@ public void whenNotifiedsigFloat() assertEquals(StructArrayInterfaceMessageType.SIG_SigFloat.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); StructFloat[] receivedparamFloat = StructFloatParcelable.unwrapArray((StructFloatParcelable[])data.getParcelableArray("paramFloat", StructFloatParcelable.class)); assertEquals(receivedparamFloat, testparamFloat); @@ -452,11 +511,32 @@ public void whenNotifiedsigString() assertEquals(StructArrayInterfaceMessageType.SIG_SigString.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); StructString[] receivedparamString = StructStringParcelable.unwrapArray((StructStringParcelable[])data.getParcelableArray("paramString", StructStringParcelable.class)); assertEquals(receivedparamString, testparamString); } + @Test + public void whenNotifiedsigEnum() + { + Enum0[] testparamEnum = new Enum0[1]; + testparamEnum[0] = Enum0.Value1; + + testedAdapterAsEventListener.onSigEnum(testparamEnum); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.SIG_SigEnum.getValue(), response.what); + Bundle data = response.getData(); + + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + + Enum0[] receivedparamEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("paramEnum", Enum0Parcelable.class)); + assertEquals(receivedparamEnum, testparamEnum); +} public void onfuncBoolRequest() throws RemoteException { @@ -602,4 +682,40 @@ public void onfuncStringRequest() throws RemoteException { assertEquals(callId, resp_data.getInt("callId", -1)); } + + public void onfuncEnumRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, StructArrayInterfaceMessageType.RPC_FuncEnumReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + Enum0[] testparamEnum = new Enum0[1]; + testparamEnum[0] = Enum0.Value1; + data.putParcelableArray("paramEnum", Enum0Parcelable.wrapArray(testparamEnum)); + Enum0[] returnedValue = new Enum0[1]; + returnedValue[0] = Enum0.Value1; + + + when(backendServiceMock.funcEnum(testparamEnum)).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcEnum(testparamEnum); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(StructArrayInterfaceMessageType.RPC_FuncEnumResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + Enum0[] receivedByClient = Enum0Parcelable.unwrapArray((Enum0Parcelable[])resp_data.getParcelableArray("result", Enum0Parcelable.class)); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + } diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java index 7c4ca08..a7946fc 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java @@ -25,7 +25,25 @@ import testbed1.testbed1_android_messenger.StructFloatParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_android_messenger.StructStructParcelable; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_android_messenger.StructEnumParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_android_messenger.StructStructWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; import testbed1.testbed1_api.Testbed1TestHelper; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.IStructInterfaceEventListener; @@ -184,10 +202,8 @@ void registerFakeActivityClient(Messenger messenger, String id) StructFloat receivedpropFloat = data.getParcelable("propFloat", StructFloatParcelable.class).getStructFloat(); StructString receivedpropString = data.getParcelable("propString", StructStringParcelable.class).getStructString(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); // assertEquals(receivedpropBool, initpropBool); // assertEquals(receivedpropInt, initpropInt); // assertEquals(receivedpropFloat, initpropFloat); @@ -229,7 +245,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceivepropBoolPropertyChangeTest() throws RemoteException { // Create and send message @@ -264,7 +279,6 @@ public void whenNotifiedpropBool() assertEquals(receivedpropBool, testpropBool); } -//TODO do not add when a property is readonly @Test public void onReceivepropIntPropertyChangeTest() throws RemoteException { // Create and send message @@ -299,7 +313,6 @@ public void whenNotifiedpropInt() assertEquals(receivedpropInt, testpropInt); } -//TODO do not add when a property is readonly @Test public void onReceivepropFloatPropertyChangeTest() throws RemoteException { // Create and send message @@ -334,7 +347,6 @@ public void whenNotifiedpropFloat() assertEquals(receivedpropFloat, testpropFloat); } -//TODO do not add when a property is readonly @Test public void onReceivepropStringPropertyChangeTest() throws RemoteException { // Create and send message @@ -382,7 +394,8 @@ public void whenNotifiedsigBool() assertEquals(StructInterfaceMessageType.SIG_SigBool.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); StructBool receivedparamBool = data.getParcelable("paramBool", StructBoolParcelable.class).getStructBool(); assertEquals(receivedparamBool, testparamBool); @@ -400,7 +413,8 @@ public void whenNotifiedsigInt() assertEquals(StructInterfaceMessageType.SIG_SigInt.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + + data.setClassLoader(StructIntParcelable.class.getClassLoader()); StructInt receivedparamInt = data.getParcelable("paramInt", StructIntParcelable.class).getStructInt(); assertEquals(receivedparamInt, testparamInt); @@ -418,7 +432,8 @@ public void whenNotifiedsigFloat() assertEquals(StructInterfaceMessageType.SIG_SigFloat.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); StructFloat receivedparamFloat = data.getParcelable("paramFloat", StructFloatParcelable.class).getStructFloat(); assertEquals(receivedparamFloat, testparamFloat); @@ -436,7 +451,8 @@ public void whenNotifiedsigString() assertEquals(StructInterfaceMessageType.SIG_SigString.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + + data.setClassLoader(StructStringParcelable.class.getClassLoader()); StructString receivedparamString = data.getParcelable("paramString", StructStringParcelable.class).getStructString(); assertEquals(receivedparamString, testparamString); diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArray2Interface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArray2Interface.java new file mode 100644 index 0000000..35dce49 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArray2Interface.java @@ -0,0 +1,100 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; + +import java.util.Collection; +import java.util.HashSet; + public abstract class AbstractStructArray2Interface implements IStructArray2Interface { + public Collection listeners = new HashSet<>(); + + public void addEventListener(IStructArray2InterfaceEventListener listener) { + listeners.add(listener); + } + public void removeEventListener(IStructArray2InterfaceEventListener listener) { + listeners.remove(listener); + } + @Override + public void firePropBoolChanged(StructBoolWithArray newValue) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onPropBoolChanged(newValue); + } + } + + @Override + public void firePropIntChanged(StructIntWithArray newValue) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onPropIntChanged(newValue); + } + } + + @Override + public void firePropFloatChanged(StructFloatWithArray newValue) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onPropFloatChanged(newValue); + } + } + + @Override + public void firePropStringChanged(StructStringWithArray newValue) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onPropStringChanged(newValue); + } + } + + @Override + public void firePropEnumChanged(StructEnumWithArray newValue) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onPropEnumChanged(newValue); + } + } + + @Override + public void fireSigBool(StructBoolWithArray paramBool) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onSigBool(paramBool); + } + } + + @Override + public void fireSigInt(StructIntWithArray paramInt) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onSigInt(paramInt); + } + } + + @Override + public void fireSigFloat(StructFloatWithArray paramFloat) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onSigFloat(paramFloat); + } + } + + @Override + public void fireSigString(StructStringWithArray paramString) { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.onSigString(paramString); + } + } + + + public void fire_readyStatusChanged(boolean isReady) + { + for (IStructArray2InterfaceEventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java index eddf89f..0fc122a 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java @@ -2,11 +2,19 @@ import testbed1.testbed1_api.IStructArrayInterfaceEventListener; import testbed1.testbed1_api.IStructArrayInterface; -//TODO imported/extern modules import testbed1.testbed1_api.StructBool; import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; import java.util.Collection; import java.util.HashSet; @@ -47,6 +55,13 @@ public void firePropStringChanged(StructString[] newValue) { } } + @Override + public void firePropEnumChanged(Enum0[] newValue) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onPropEnumChanged(newValue); + } + } + @Override public void fireSigBool(StructBool[] paramBool) { for (IStructArrayInterfaceEventListener listener : listeners) { @@ -75,6 +90,13 @@ public void fireSigString(StructString[] paramString) { } } + @Override + public void fireSigEnum(Enum0[] paramEnum) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onSigEnum(paramEnum); + } + } + public void fire_readyStatusChanged(boolean isReady) { diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java index f4a9721..945366c 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java @@ -2,11 +2,19 @@ import testbed1.testbed1_api.IStructInterfaceEventListener; import testbed1.testbed1_api.IStructInterface; -//TODO imported/extern modules import testbed1.testbed1_api.StructBool; import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; import java.util.Collection; import java.util.HashSet; diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Enum0.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Enum0.java new file mode 100644 index 0000000..c9a0735 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Enum0.java @@ -0,0 +1,30 @@ +package testbed1.testbed1_api; +import com.fasterxml.jackson.annotation.JsonProperty; + + +public enum Enum0 +{ + @JsonProperty("0") + Value0(0), + @JsonProperty("1") + Value1(1), + @JsonProperty("2") + Value2(2); + + private final int value; + + Enum0(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Enum0 fromValue(int value) { + for (Enum0 e : values()) { + if (e.value == value) return e; + } + throw new IllegalArgumentException("Unknown int value: " + value); + } +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2Interface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2Interface.java new file mode 100644 index 0000000..e5afcc9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2Interface.java @@ -0,0 +1,63 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; + +import java.util.concurrent.CompletableFuture; + + + public interface IStructArray2Interface { + // properties + void setPropBool(StructBoolWithArray propBool); + StructBoolWithArray getPropBool(); + void firePropBoolChanged(StructBoolWithArray newValue); + + void setPropInt(StructIntWithArray propInt); + StructIntWithArray getPropInt(); + void firePropIntChanged(StructIntWithArray newValue); + + void setPropFloat(StructFloatWithArray propFloat); + StructFloatWithArray getPropFloat(); + void firePropFloatChanged(StructFloatWithArray newValue); + + void setPropString(StructStringWithArray propString); + StructStringWithArray getPropString(); + void firePropStringChanged(StructStringWithArray newValue); + + void setPropEnum(StructEnumWithArray propEnum); + StructEnumWithArray getPropEnum(); + void firePropEnumChanged(StructEnumWithArray newValue); + + // methods + StructBool[] funcBool(StructBoolWithArray paramBool); + CompletableFuture funcBoolAsync(StructBoolWithArray paramBool); + StructInt[] funcInt(StructIntWithArray paramInt); + CompletableFuture funcIntAsync(StructIntWithArray paramInt); + StructFloat[] funcFloat(StructFloatWithArray paramFloat); + CompletableFuture funcFloatAsync(StructFloatWithArray paramFloat); + StructString[] funcString(StructStringWithArray paramString); + CompletableFuture funcStringAsync(StructStringWithArray paramString); + Enum0[] funcEnum(StructEnumWithArray paramEnum); + CompletableFuture funcEnumAsync(StructEnumWithArray paramEnum); + public void fireSigBool(StructBoolWithArray paramBool); + public void fireSigInt(StructIntWithArray paramInt); + public void fireSigFloat(StructFloatWithArray paramFloat); + public void fireSigString(StructStringWithArray paramString); + boolean _isReady(); + // signal listeners + public void fire_readyStatusChanged(boolean isReady); + void addEventListener(IStructArray2InterfaceEventListener listener); + void removeEventListener(IStructArray2InterfaceEventListener listener); + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2InterfaceEventListener.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2InterfaceEventListener.java new file mode 100644 index 0000000..2229f49 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArray2InterfaceEventListener.java @@ -0,0 +1,27 @@ +package testbed1.testbed1_api; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; + + public interface IStructArray2InterfaceEventListener { + void onPropBoolChanged(StructBoolWithArray newValue); + void onPropIntChanged(StructIntWithArray newValue); + void onPropFloatChanged(StructFloatWithArray newValue); + void onPropStringChanged(StructStringWithArray newValue); + void onPropEnumChanged(StructEnumWithArray newValue); + void onSigBool(StructBoolWithArray paramBool); + void onSigInt(StructIntWithArray paramInt); + void onSigFloat(StructFloatWithArray paramFloat); + void onSigString(StructStringWithArray paramString); + void on_readyStatusChanged(boolean isReady); + } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java index 533ed1c..7bebca6 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java @@ -5,6 +5,15 @@ import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; import java.util.concurrent.CompletableFuture; @@ -27,6 +36,10 @@ public interface IStructArrayInterface { StructString[] getPropString(); void firePropStringChanged(StructString[] newValue); + void setPropEnum(Enum0[] propEnum); + Enum0[] getPropEnum(); + void firePropEnumChanged(Enum0[] newValue); + // methods StructBool[] funcBool(StructBool[] paramBool); CompletableFuture funcBoolAsync(StructBool[] paramBool); @@ -36,10 +49,13 @@ public interface IStructArrayInterface { CompletableFuture funcFloatAsync(StructFloat[] paramFloat); StructString[] funcString(StructString[] paramString); CompletableFuture funcStringAsync(StructString[] paramString); + Enum0[] funcEnum(Enum0[] paramEnum); + CompletableFuture funcEnumAsync(Enum0[] paramEnum); public void fireSigBool(StructBool[] paramBool); public void fireSigInt(StructInt[] paramInt); public void fireSigFloat(StructFloat[] paramFloat); public void fireSigString(StructString[] paramString); + public void fireSigEnum(Enum0[] paramEnum); boolean _isReady(); // signal listeners public void fire_readyStatusChanged(boolean isReady); diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java index 7e31899..a97803f 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java @@ -3,15 +3,26 @@ import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; public interface IStructArrayInterfaceEventListener { void onPropBoolChanged(StructBool[] newValue); void onPropIntChanged(StructInt[] newValue); void onPropFloatChanged(StructFloat[] newValue); void onPropStringChanged(StructString[] newValue); + void onPropEnumChanged(Enum0[] newValue); void onSigBool(StructBool[] paramBool); void onSigInt(StructInt[] paramInt); void onSigFloat(StructFloat[] paramFloat); void onSigString(StructString[] paramString); + void onSigEnum(Enum0[] paramEnum); void on_readyStatusChanged(boolean isReady); } diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java index a4ce431..b962c8d 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java @@ -5,6 +5,15 @@ import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java index 66dd3ac..a292190 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java @@ -3,6 +3,15 @@ import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStruct; +import testbed1.testbed1_api.StructEnum; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_api.StructStructWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.Enum0; public interface IStructInterfaceEventListener { void onPropBoolChanged(StructBool newValue); diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBoolWithArray.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBoolWithArray.java new file mode 100644 index 0000000..3e115bf --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructBoolWithArray.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructBoolWithArray { + + public StructBoolWithArray(boolean[] fieldBool) + { + this.fieldBool = fieldBool; + } + + public StructBoolWithArray() + { + this.fieldBool = new boolean[0]; + } + @JsonProperty("field_bool") + public boolean[] fieldBool; + + public StructBoolWithArray(StructBoolWithArray other) + { + this.fieldBool = java.util.Arrays.copyOf(other.fieldBool, other.fieldBool.length); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructBoolWithArray)) return false; + StructBoolWithArray other = (StructBoolWithArray) o; + + return + Arrays.equals(this.fieldBool, other.fieldBool); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Arrays.hashCode(fieldBool); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnum.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnum.java new file mode 100644 index 0000000..fcfab07 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnum.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructEnum { + + public StructEnum(Enum0 fieldEnum) + { + this.fieldEnum = fieldEnum; + } + + public StructEnum() + { + this.fieldEnum = Enum0.values()[0]; + } + @JsonProperty("field_enum") + public Enum0 fieldEnum; + + public StructEnum(StructEnum other) + { + this.fieldEnum = other.fieldEnum; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructEnum)) return false; + StructEnum other = (StructEnum) o; + + return + this.fieldEnum == other.fieldEnum; + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Objects.hashCode(fieldEnum); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnumWithArray.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnumWithArray.java new file mode 100644 index 0000000..de8c8b8 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructEnumWithArray.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructEnumWithArray { + + public StructEnumWithArray(Enum0[] fieldEnum) + { + this.fieldEnum = fieldEnum; + } + + public StructEnumWithArray() + { + this.fieldEnum = new Enum0[0]; + } + @JsonProperty("field_enum") + public Enum0[] fieldEnum; + + public StructEnumWithArray(StructEnumWithArray other) + { + this.fieldEnum = java.util.Arrays.copyOf(other.fieldEnum, other.fieldEnum.length); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructEnumWithArray)) return false; + StructEnumWithArray other = (StructEnumWithArray) o; + + return + Arrays.equals(this.fieldEnum, other.fieldEnum); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Arrays.hashCode(fieldEnum); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloatWithArray.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloatWithArray.java new file mode 100644 index 0000000..a04700a --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructFloatWithArray.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructFloatWithArray { + + public StructFloatWithArray(float[] fieldFloat) + { + this.fieldFloat = fieldFloat; + } + + public StructFloatWithArray() + { + this.fieldFloat = new float[0]; + } + @JsonProperty("field_float") + public float[] fieldFloat; + + public StructFloatWithArray(StructFloatWithArray other) + { + this.fieldFloat = java.util.Arrays.copyOf(other.fieldFloat, other.fieldFloat.length); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructFloatWithArray)) return false; + StructFloatWithArray other = (StructFloatWithArray) o; + + return + Arrays.equals(this.fieldFloat, other.fieldFloat); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Arrays.hashCode(fieldFloat); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructIntWithArray.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructIntWithArray.java new file mode 100644 index 0000000..2336863 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructIntWithArray.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructIntWithArray { + + public StructIntWithArray(int[] fieldInt) + { + this.fieldInt = fieldInt; + } + + public StructIntWithArray() + { + this.fieldInt = new int[0]; + } + @JsonProperty("field_int") + public int[] fieldInt; + + public StructIntWithArray(StructIntWithArray other) + { + this.fieldInt = java.util.Arrays.copyOf(other.fieldInt, other.fieldInt.length); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructIntWithArray)) return false; + StructIntWithArray other = (StructIntWithArray) o; + + return + Arrays.equals(this.fieldInt, other.fieldInt); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Arrays.hashCode(fieldInt); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStringWithArray.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStringWithArray.java new file mode 100644 index 0000000..5a9ee6f --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStringWithArray.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructStringWithArray { + + public StructStringWithArray(String[] fieldString) + { + this.fieldString = fieldString; + } + + public StructStringWithArray() + { + this.fieldString = new String[0]; + } + @JsonProperty("field_string") + public String[] fieldString; + + public StructStringWithArray(StructStringWithArray other) + { + this.fieldString = java.util.Arrays.copyOf(other.fieldString, other.fieldString.length); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructStringWithArray)) return false; + StructStringWithArray other = (StructStringWithArray) o; + + return + Arrays.equals(this.fieldString, other.fieldString); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Arrays.hashCode(fieldString); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStruct.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStruct.java new file mode 100644 index 0000000..950bde1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStruct.java @@ -0,0 +1,44 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructStruct { + + public StructStruct(StructString fieldString) + { + this.fieldString = fieldString; + } + + public StructStruct() + { + this.fieldString = new StructString(); + } + @JsonProperty("field_string") + public StructString fieldString; + + public StructStruct(StructStruct other) + { + this.fieldString = new StructString(other.fieldString); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructStruct)) return false; + StructStruct other = (StructStruct) o; + + return + Objects.equals(this.fieldString, other.fieldString); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Objects.hashCode(fieldString); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStructWithArray.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStructWithArray.java new file mode 100644 index 0000000..a0b9969 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/StructStructWithArray.java @@ -0,0 +1,48 @@ +package testbed1.testbed1_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class StructStructWithArray { + + public StructStructWithArray(StructStringWithArray[] fieldStruct) + { + this.fieldStruct = fieldStruct; + } + + public StructStructWithArray() + { + this.fieldStruct = new StructStringWithArray[0]; + } + @JsonProperty("field_struct") + public StructStringWithArray[] fieldStruct; + + public StructStructWithArray(StructStructWithArray other) + { + this.fieldStruct = new StructStringWithArray[other.fieldStruct.length]; + for (int i = 0; i < other.fieldStruct.length; i++) + { + this.fieldStruct[i] = new StructStringWithArray(other.fieldStruct[i]); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StructStructWithArray)) return false; + StructStructWithArray other = (StructStructWithArray) o; + + return + Arrays.equals(this.fieldStruct, other.fieldStruct); + } + + @Override + public int hashCode() { + int result = 7; + result = 31 * result + Arrays.hashCode(fieldStruct); + return result; + } + + +} diff --git a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java index 7d618c6..5663610 100644 --- a/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Arrays; - public class Testbed1TestHelper { @@ -36,4 +35,116 @@ static public StructString makeTestStructString() return testStruct; } + static public StructStruct makeTestStructStruct() + { + StructStruct testStruct = new StructStruct(); + testStruct.fieldString = Testbed1TestHelper.makeTestStructString(); + return testStruct; + } + + static public StructEnum makeTestStructEnum() + { + StructEnum testStruct = new StructEnum(); + testStruct.fieldEnum = Enum0.Value1; + return testStruct; + } + + static public StructBoolWithArray makeTestStructBoolWithArray() + { + StructBoolWithArray testStruct = new StructBoolWithArray(); + testStruct.fieldBool = new boolean[1]; + testStruct.fieldBool[0] = true; + return testStruct; + } + + static public StructIntWithArray makeTestStructIntWithArray() + { + StructIntWithArray testStruct = new StructIntWithArray(); + testStruct.fieldInt = new int[1]; + testStruct.fieldInt[0] = 1; + return testStruct; + } + + static public StructFloatWithArray makeTestStructFloatWithArray() + { + StructFloatWithArray testStruct = new StructFloatWithArray(); + testStruct.fieldFloat = new float[1]; + testStruct.fieldFloat[0] = 1.0f; + return testStruct; + } + + static public StructStringWithArray makeTestStructStringWithArray() + { + StructStringWithArray testStruct = new StructStringWithArray(); + testStruct.fieldString = new String[1]; + testStruct.fieldString[0] = new String("xyz"); + return testStruct; + } + + static public StructStructWithArray makeTestStructStructWithArray() + { + StructStructWithArray testStruct = new StructStructWithArray(); + testStruct.fieldStruct = new StructStringWithArray[1]; + testStruct.fieldStruct[0] = Testbed1TestHelper.makeTestStructStringWithArray(); + return testStruct; + } + + static public StructEnumWithArray makeTestStructEnumWithArray() + { + StructEnumWithArray testStruct = new StructEnumWithArray(); + testStruct.fieldEnum = new Enum0[1]; + testStruct.fieldEnum[0] = Enum0.Value1; + return testStruct; + } + + static public IStructInterface makeTestStructInterface(IStructInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + StructBool localpropBool = Testbed1TestHelper.makeTestStructBool(); + testObjToFill.setPropBool(localpropBool); + StructInt localpropInt = Testbed1TestHelper.makeTestStructInt(); + testObjToFill.setPropInt(localpropInt); + StructFloat localpropFloat = Testbed1TestHelper.makeTestStructFloat(); + testObjToFill.setPropFloat(localpropFloat); + StructString localpropString = Testbed1TestHelper.makeTestStructString(); + testObjToFill.setPropString(localpropString); + return testObjToFill; + } + + static public IStructArrayInterface makeTestStructArrayInterface(IStructArrayInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + StructBool[] localpropBool = new StructBool[1]; + localpropBool[0] = Testbed1TestHelper.makeTestStructBool(); + testObjToFill.setPropBool(localpropBool); + StructInt[] localpropInt = new StructInt[1]; + localpropInt[0] = Testbed1TestHelper.makeTestStructInt(); + testObjToFill.setPropInt(localpropInt); + StructFloat[] localpropFloat = new StructFloat[1]; + localpropFloat[0] = Testbed1TestHelper.makeTestStructFloat(); + testObjToFill.setPropFloat(localpropFloat); + StructString[] localpropString = new StructString[1]; + localpropString[0] = Testbed1TestHelper.makeTestStructString(); + testObjToFill.setPropString(localpropString); + Enum0[] localpropEnum = new Enum0[1]; + localpropEnum[0] = Enum0.Value1; + testObjToFill.setPropEnum(localpropEnum); + return testObjToFill; + } + + static public IStructArray2Interface makeTestStructArray2Interface(IStructArray2Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + StructBoolWithArray localpropBool = Testbed1TestHelper.makeTestStructBoolWithArray(); + testObjToFill.setPropBool(localpropBool); + StructIntWithArray localpropInt = Testbed1TestHelper.makeTestStructIntWithArray(); + testObjToFill.setPropInt(localpropInt); + StructFloatWithArray localpropFloat = Testbed1TestHelper.makeTestStructFloatWithArray(); + testObjToFill.setPropFloat(localpropFloat); + StructStringWithArray localpropString = Testbed1TestHelper.makeTestStructStringWithArray(); + testObjToFill.setPropString(localpropString); + StructEnumWithArray localpropEnum = Testbed1TestHelper.makeTestStructEnumWithArray(); + testObjToFill.setPropEnum(localpropEnum); + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_client_example/build.gradle b/goldenmaster/testbed1/testbed1_client_example/build.gradle index b14c149..49a6606 100644 --- a/goldenmaster/testbed1/testbed1_client_example/build.gradle +++ b/goldenmaster/testbed1/testbed1_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "testbed1" +version = "1.0.0" + android { namespace 'testbed1.testbed1_client_example' compileSdk 35 diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java index 9b76fde..d2e9d13 100644 --- a/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java @@ -13,17 +13,14 @@ //TODO for each interface there coudl be a tab? now only first one is added import testbed1.testbed1_android_client.StructInterfaceClient; - -//import message type and parcelabe types import testbed1.testbed1_api.StructBool; import testbed1.testbed1_android_messenger.StructBoolParcelable; -import testbed1.testbed1_api.StructInt; -import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; - import testbed1.testbed1_api.IStructInterfaceEventListener; import java.util.concurrent.CompletableFuture; @@ -351,4 +348,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_impl/additions.gradle b/goldenmaster/testbed1/testbed1_impl/additions.gradle index bec9b44..3213a11 100644 --- a/goldenmaster/testbed1/testbed1_impl/additions.gradle +++ b/goldenmaster/testbed1/testbed1_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'testbed1.testbed1_impl' diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java new file mode 100644 index 0000000..ce3f665 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java @@ -0,0 +1,266 @@ +package testbed1.testbed1_impl; + +import android.os.Messenger; +import android.util.Log; + +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_api.StructStringWithArray; + + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.Arrays; + + +public class StructArray2InterfaceService extends AbstractStructArray2Interface { + + private final static String TAG = "StructArray2InterfaceService"; + private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private StructBoolWithArray m_propBool = new StructBoolWithArray(); + private StructIntWithArray m_propInt = new StructIntWithArray(); + private StructFloatWithArray m_propFloat = new StructFloatWithArray(); + private StructStringWithArray m_propString = new StructStringWithArray(); + private StructEnumWithArray m_propEnum = new StructEnumWithArray(); + + public StructArray2InterfaceService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(StructBoolWithArray propBool) + { + Log.i(TAG, "request setPropBool called "); + if (! m_propBool.equals(propBool)) + { + m_propBool = propBool; + onPropBoolChanged(m_propBool); + } + + } + + @Override + public StructBoolWithArray getPropBool() + { + Log.i(TAG, "request getPropBool called,"); + return m_propBool; + } + + + @Override + public void setPropInt(StructIntWithArray propInt) + { + Log.i(TAG, "request setPropInt called "); + if (! m_propInt.equals(propInt)) + { + m_propInt = propInt; + onPropIntChanged(m_propInt); + } + + } + + @Override + public StructIntWithArray getPropInt() + { + Log.i(TAG, "request getPropInt called,"); + return m_propInt; + } + + + @Override + public void setPropFloat(StructFloatWithArray propFloat) + { + Log.i(TAG, "request setPropFloat called "); + if (! m_propFloat.equals(propFloat)) + { + m_propFloat = propFloat; + onPropFloatChanged(m_propFloat); + } + + } + + @Override + public StructFloatWithArray getPropFloat() + { + Log.i(TAG, "request getPropFloat called,"); + return m_propFloat; + } + + + @Override + public void setPropString(StructStringWithArray propString) + { + Log.i(TAG, "request setPropString called "); + if (! m_propString.equals(propString)) + { + m_propString = propString; + onPropStringChanged(m_propString); + } + + } + + @Override + public StructStringWithArray getPropString() + { + Log.i(TAG, "request getPropString called,"); + return m_propString; + } + + + @Override + public void setPropEnum(StructEnumWithArray propEnum) + { + Log.i(TAG, "request setPropEnum called "); + if (! m_propEnum.equals(propEnum)) + { + m_propEnum = propEnum; + onPropEnumChanged(m_propEnum); + } + + } + + @Override + public StructEnumWithArray getPropEnum() + { + Log.i(TAG, "request getPropEnum called,"); + return m_propEnum; + } + + + // methods + + @Override + public StructBool[] funcBool(StructBoolWithArray paramBool) { + Log.w(TAG, "request method funcBool called, returnig default"); + return new StructBool[]{}; + } + + @Override + public CompletableFuture funcBoolAsync(StructBoolWithArray paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public StructInt[] funcInt(StructIntWithArray paramInt) { + Log.w(TAG, "request method funcInt called, returnig default"); + return new StructInt[]{}; + } + + @Override + public CompletableFuture funcIntAsync(StructIntWithArray paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public StructFloat[] funcFloat(StructFloatWithArray paramFloat) { + Log.w(TAG, "request method funcFloat called, returnig default"); + return new StructFloat[]{}; + } + + @Override + public CompletableFuture funcFloatAsync(StructFloatWithArray paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public StructString[] funcString(StructStringWithArray paramString) { + Log.w(TAG, "request method funcString called, returnig default"); + return new StructString[]{}; + } + + @Override + public CompletableFuture funcStringAsync(StructStringWithArray paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public Enum0[] funcEnum(StructEnumWithArray paramEnum) { + Log.w(TAG, "request method funcEnum called, returnig default"); + return new Enum0[]{}; + } + + @Override + public CompletableFuture funcEnumAsync(StructEnumWithArray paramEnum) { + return CompletableFuture.supplyAsync( + () -> {return funcEnum(paramEnum); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + //In theory event listener interface + private void onPropBoolChanged(StructBoolWithArray newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + private void onPropIntChanged(StructIntWithArray newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + private void onPropFloatChanged(StructFloatWithArray newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + private void onPropStringChanged(StructStringWithArray newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + private void onPropEnumChanged(StructEnumWithArray newValue) + { + Log.i(TAG, "onPropEnumChanged, will pass notification to all listeners"); + firePropEnumChanged(newValue); + } + public void onSigBool(StructBoolWithArray paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(StructIntWithArray paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloatWithArray paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigString(StructStringWithArray paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java index f3e2da5..736b858 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java @@ -6,9 +6,10 @@ import testbed1.testbed1_api.IStructArrayInterface; import testbed1.testbed1_api.AbstractStructArrayInterface; import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.Enum0; import testbed1.testbed1_api.StructBool; -import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructString; @@ -32,6 +33,7 @@ public class StructArrayInterfaceService extends AbstractStructArrayInterface { private StructInt[] m_propInt = new StructInt[]{}; private StructFloat[] m_propFloat = new StructFloat[]{}; private StructString[] m_propString = new StructString[]{}; + private Enum0[] m_propEnum = new Enum0[]{}; public StructArrayInterfaceService() { @@ -117,6 +119,26 @@ public StructString[] getPropString() } + @Override + public void setPropEnum(Enum0[] propEnum) + { + Log.i(TAG, "request setPropEnum called "); + if (! Arrays.equals(m_propEnum, propEnum)) + { + m_propEnum = propEnum; + onPropEnumChanged(m_propEnum); + } + + } + + @Override + public Enum0[] getPropEnum() + { + Log.i(TAG, "request getPropEnum called,"); + return m_propEnum; + } + + // methods @Override @@ -169,6 +191,19 @@ public CompletableFuture funcStringAsync(StructString[] paramSt return CompletableFuture.supplyAsync( () -> {return funcString(paramString); }, executor); + } + + @Override + public Enum0[] funcEnum(Enum0[] paramEnum) { + Log.w(TAG, "request method funcEnum called, returnig default"); + return new Enum0[]{}; + } + + @Override + public CompletableFuture funcEnumAsync(Enum0[] paramEnum) { + return CompletableFuture.supplyAsync( + () -> {return funcEnum(paramEnum); }, + executor); } @Override @@ -197,6 +232,11 @@ private void onPropStringChanged(StructString[] newValue) Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); firePropStringChanged(newValue); } + private void onPropEnumChanged(Enum0[] newValue) + { + Log.i(TAG, "onPropEnumChanged, will pass notification to all listeners"); + firePropEnumChanged(newValue); + } public void onSigBool(StructBool[] paramBool) { Log.i(TAG, "onSigBool, will pass notification to all listeners"); @@ -217,5 +257,10 @@ public void onSigString(StructString[] paramString) Log.i(TAG, "onSigString, will pass notification to all listeners"); fireSigString(paramString); } + public void onSigEnum(Enum0[] paramEnum) + { + Log.i(TAG, "onSigEnum, will pass notification to all listeners"); + fireSigEnum(paramEnum); + } } \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java index 81962e6..bf6dc7b 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java @@ -7,8 +7,8 @@ import testbed1.testbed1_api.AbstractStructInterface; import testbed1.testbed1_api.IStructInterfaceEventListener; import testbed1.testbed1_api.StructBool; -import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructInt; import testbed1.testbed1_api.StructString; diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java new file mode 100644 index 0000000..2151ceb --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java @@ -0,0 +1,314 @@ +package testbed1.testbed1jniclient; + +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; + +import testbed1.testbed1_android_client.StructArray2InterfaceClient; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; +import android.content.Context; + +import android.os.Bundle; +import java.util.concurrent.CompletableFuture; +import android.util.Log; + + + +public class StructArray2InterfaceJniClient extends AbstractStructArray2Interface implements IStructArray2InterfaceEventListener +{ + + private static final String TAG = "StructArray2InterfaceJniClient"; + + private StructArray2InterfaceClient mMessengerClient = null; + + + private static String ModuleName = "testbed1.testbed1jniservice.StructArray2InterfaceJniService"; + private String lastServicePackage =""; + + @Override + public boolean _isReady() + { + return mMessengerClient._isReady(); + } + @Override + public void setPropBool(StructBoolWithArray propBool) + { + Log.i(TAG, "got request from ue, setPropBool" + (propBool)); + mMessengerClient.setPropBool(propBool); + } + @Override + public StructBoolWithArray getPropBool() + { + Log.i(TAG, "got request from ue, getPropBool"); + return mMessengerClient.getPropBool(); + } + + @Override + public void setPropInt(StructIntWithArray propInt) + { + Log.i(TAG, "got request from ue, setPropInt" + (propInt)); + mMessengerClient.setPropInt(propInt); + } + @Override + public StructIntWithArray getPropInt() + { + Log.i(TAG, "got request from ue, getPropInt"); + return mMessengerClient.getPropInt(); + } + + @Override + public void setPropFloat(StructFloatWithArray propFloat) + { + Log.i(TAG, "got request from ue, setPropFloat" + (propFloat)); + mMessengerClient.setPropFloat(propFloat); + } + @Override + public StructFloatWithArray getPropFloat() + { + Log.i(TAG, "got request from ue, getPropFloat"); + return mMessengerClient.getPropFloat(); + } + + @Override + public void setPropString(StructStringWithArray propString) + { + Log.i(TAG, "got request from ue, setPropString" + (propString)); + mMessengerClient.setPropString(propString); + } + @Override + public StructStringWithArray getPropString() + { + Log.i(TAG, "got request from ue, getPropString"); + return mMessengerClient.getPropString(); + } + + @Override + public void setPropEnum(StructEnumWithArray propEnum) + { + Log.i(TAG, "got request from ue, setPropEnum" + (propEnum)); + mMessengerClient.setPropEnum(propEnum); + } + @Override + public StructEnumWithArray getPropEnum() + { + Log.i(TAG, "got request from ue, getPropEnum"); + return mMessengerClient.getPropEnum(); + } + + public StructBool[] funcBool(StructBoolWithArray paramBool) + { + Log.v(TAG, "Blocking callfuncBool - should not be used "); + return mMessengerClient.funcBool(paramBool); + } + + public void funcBoolAsync(String callId, StructBoolWithArray paramBool){ + Log.v(TAG, "non blocking call funcBool "); + mMessengerClient.funcBoolAsync(paramBool).thenAccept(i -> { + nativeOnFuncBoolResult(i, callId);}); + } + + //Should not be called directly, use funcBoolAsync(String callId, StructBoolWithArray paramBool) + public CompletableFuture funcBoolAsync(StructBoolWithArray paramBool) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcBoolAsync(paramBool); + } + public StructInt[] funcInt(StructIntWithArray paramInt) + { + Log.v(TAG, "Blocking callfuncInt - should not be used "); + return mMessengerClient.funcInt(paramInt); + } + + public void funcIntAsync(String callId, StructIntWithArray paramInt){ + Log.v(TAG, "non blocking call funcInt "); + mMessengerClient.funcIntAsync(paramInt).thenAccept(i -> { + nativeOnFuncIntResult(i, callId);}); + } + + //Should not be called directly, use funcIntAsync(String callId, StructIntWithArray paramInt) + public CompletableFuture funcIntAsync(StructIntWithArray paramInt) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcIntAsync(paramInt); + } + public StructFloat[] funcFloat(StructFloatWithArray paramFloat) + { + Log.v(TAG, "Blocking callfuncFloat - should not be used "); + return mMessengerClient.funcFloat(paramFloat); + } + + public void funcFloatAsync(String callId, StructFloatWithArray paramFloat){ + Log.v(TAG, "non blocking call funcFloat "); + mMessengerClient.funcFloatAsync(paramFloat).thenAccept(i -> { + nativeOnFuncFloatResult(i, callId);}); + } + + //Should not be called directly, use funcFloatAsync(String callId, StructFloatWithArray paramFloat) + public CompletableFuture funcFloatAsync(StructFloatWithArray paramFloat) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcFloatAsync(paramFloat); + } + public StructString[] funcString(StructStringWithArray paramString) + { + Log.v(TAG, "Blocking callfuncString - should not be used "); + return mMessengerClient.funcString(paramString); + } + + public void funcStringAsync(String callId, StructStringWithArray paramString){ + Log.v(TAG, "non blocking call funcString "); + mMessengerClient.funcStringAsync(paramString).thenAccept(i -> { + nativeOnFuncStringResult(i, callId);}); + } + + //Should not be called directly, use funcStringAsync(String callId, StructStringWithArray paramString) + public CompletableFuture funcStringAsync(StructStringWithArray paramString) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcStringAsync(paramString); + } + public Enum0[] funcEnum(StructEnumWithArray paramEnum) + { + Log.v(TAG, "Blocking callfuncEnum - should not be used "); + return mMessengerClient.funcEnum(paramEnum); + } + + public void funcEnumAsync(String callId, StructEnumWithArray paramEnum){ + Log.v(TAG, "non blocking call funcEnum "); + mMessengerClient.funcEnumAsync(paramEnum).thenAccept(i -> { + nativeOnFuncEnumResult(i, callId);}); + } + + //Should not be called directly, use funcEnumAsync(String callId, StructEnumWithArray paramEnum) + public CompletableFuture funcEnumAsync(StructEnumWithArray paramEnum) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcEnumAsync(paramEnum); + } + + public boolean bind(Context ctx, String packageName, String connectionID){ + Log.v(TAG, "natice client: bind " + packageName); + return initServiceConnection(ctx, packageName, connectionID); + } + + public void unbind(){ + Log.v(TAG, "native client: unbind " + lastServicePackage); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new StructArray2InterfaceClient(ctx, connectionID); + Log.w(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + unbind(); + } + lastServicePackage = servicePackage; + boolean res = mMessengerClient.bindToService(lastServicePackage); + Log.v(TAG, "Bind " + res+": to "+lastServicePackage); + return res; + } + + @Override + public void on_readyStatusChanged(boolean isReady) { + Log.w(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(StructBoolWithArray newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(StructIntWithArray newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropFloatChanged(StructFloatWithArray newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropStringChanged(StructStringWithArray newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onPropEnumChanged(StructEnumWithArray newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropEnumChanged(newValue); + } + @Override + public void onSigBool(StructBoolWithArray paramBool) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(StructIntWithArray paramInt) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigFloat(StructFloatWithArray paramFloat) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigString(StructStringWithArray paramString) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + nativeOnSigString(paramString); + } + private native void nativeOnPropBoolChanged(StructBoolWithArray propBool); + private native void nativeOnPropIntChanged(StructIntWithArray propInt); + private native void nativeOnPropFloatChanged(StructFloatWithArray propFloat); + private native void nativeOnPropStringChanged(StructStringWithArray propString); + private native void nativeOnPropEnumChanged(StructEnumWithArray propEnum); + private native void nativeOnSigBool(StructBoolWithArray paramBool); + private native void nativeOnSigInt(StructIntWithArray paramInt); + private native void nativeOnSigFloat(StructFloatWithArray paramFloat); + private native void nativeOnSigString(StructStringWithArray paramString); + private native void nativeOnFuncBoolResult(StructBool[] result, String callId); + private native void nativeOnFuncIntResult(StructInt[] result, String callId); + private native void nativeOnFuncFloatResult(StructFloat[] result, String callId); + private native void nativeOnFuncStringResult(StructString[] result, String callId); + private native void nativeOnFuncEnumResult(Enum0[] result, String callId); + private native void nativeIsReady(boolean isReady); +} diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java index 887eb91..816892e 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java @@ -5,10 +5,16 @@ import testbed1.testbed1_api.IStructArrayInterfaceEventListener; import testbed1.testbed1_android_client.StructArrayInterfaceClient; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.StructBool; -import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructBoolParcelable; import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; import android.content.Context; import android.os.Bundle; @@ -85,6 +91,19 @@ public StructString[] getPropString() return mMessengerClient.getPropString(); } + @Override + public void setPropEnum(Enum0[] propEnum) + { + Log.i(TAG, "got request from ue, setPropEnum" + (propEnum)); + mMessengerClient.setPropEnum(propEnum); + } + @Override + public Enum0[] getPropEnum() + { + Log.i(TAG, "got request from ue, getPropEnum"); + return mMessengerClient.getPropEnum(); + } + public StructBool[] funcBool(StructBool[] paramBool) { Log.v(TAG, "Blocking callfuncBool - should not be used "); @@ -157,6 +176,24 @@ public CompletableFuture funcStringAsync(StructString[] paramStr Log.v(TAG, "NON Blocking call method "); return mMessengerClient.funcStringAsync(paramString); } + public Enum0[] funcEnum(Enum0[] paramEnum) + { + Log.v(TAG, "Blocking callfuncEnum - should not be used "); + return mMessengerClient.funcEnum(paramEnum); + } + + public void funcEnumAsync(String callId, Enum0[] paramEnum){ + Log.v(TAG, "non blocking call funcEnum "); + mMessengerClient.funcEnumAsync(paramEnum).thenAccept(i -> { + nativeOnFuncEnumResult(i, callId);}); + } + + //Should not be called directly, use funcEnumAsync(String callId, Enum0[] paramEnum) + public CompletableFuture funcEnumAsync(Enum0[] paramEnum) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcEnumAsync(paramEnum); + } public boolean bind(Context ctx, String packageName, String connectionID){ Log.v(TAG, "natice client: bind " + packageName); @@ -220,6 +257,12 @@ public void onPropStringChanged(StructString[] newValue) nativeOnPropStringChanged(newValue); } @Override + public void onPropEnumChanged(Enum0[] newValue) + { + Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropEnumChanged(newValue); + } + @Override public void onSigBool(StructBool[] paramBool) { Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); @@ -242,18 +285,27 @@ public void onSigString(StructString[] paramString) { Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); nativeOnSigString(paramString); + } + @Override + public void onSigEnum(Enum0[] paramEnum) + { + Log.w(TAG, "NOTIFICATION from messenger client Signal sigEnum "+ " " + paramEnum); + nativeOnSigEnum(paramEnum); } private native void nativeOnPropBoolChanged(StructBool[] propBool); private native void nativeOnPropIntChanged(StructInt[] propInt); private native void nativeOnPropFloatChanged(StructFloat[] propFloat); private native void nativeOnPropStringChanged(StructString[] propString); + private native void nativeOnPropEnumChanged(Enum0[] propEnum); private native void nativeOnSigBool(StructBool[] paramBool); private native void nativeOnSigInt(StructInt[] paramInt); private native void nativeOnSigFloat(StructFloat[] paramFloat); private native void nativeOnSigString(StructString[] paramString); + private native void nativeOnSigEnum(Enum0[] paramEnum); private native void nativeOnFuncBoolResult(StructBool[] result, String callId); private native void nativeOnFuncIntResult(StructInt[] result, String callId); private native void nativeOnFuncFloatResult(StructFloat[] result, String callId); private native void nativeOnFuncStringResult(StructString[] result, String callId); + private native void nativeOnFuncEnumResult(Enum0[] result, String callId); private native void nativeIsReady(boolean isReady); } diff --git a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java index 5abf194..d90a719 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java @@ -6,9 +6,13 @@ import testbed1.testbed1_android_client.StructInterfaceClient; import testbed1.testbed1_api.StructBool; -import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructBoolParcelable; import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java new file mode 100644 index 0000000..78444a9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java @@ -0,0 +1,273 @@ +package testbed1.testbed1jniservice; + +import android.os.Messenger; +import android.util.Log; + +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_android_messenger.StructBoolParcelable; +import testbed1.testbed1_api.StructBoolWithArray; +import testbed1.testbed1_android_messenger.StructBoolWithArrayParcelable; +import testbed1.testbed1_api.StructEnumWithArray; +import testbed1.testbed1_android_messenger.StructEnumWithArrayParcelable; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructFloatWithArray; +import testbed1.testbed1_android_messenger.StructFloatWithArrayParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; +import testbed1.testbed1_api.StructIntWithArray; +import testbed1.testbed1_android_messenger.StructIntWithArrayParcelable; +import testbed1.testbed1_api.StructString; +import testbed1.testbed1_android_messenger.StructStringParcelable; +import testbed1.testbed1_api.StructStringWithArray; +import testbed1.testbed1_android_messenger.StructStringWithArrayParcelable; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + + +public class StructArray2InterfaceJniService extends AbstractStructArray2Interface { + + + private final static String TAG = "StructArray2InterfaceJniService"; + private static boolean isServiceReady = false; + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + public StructArray2InterfaceJniService() + { + fire_readyStatusChanged(true); + } + @Override + public void setPropBool(StructBoolWithArray propBool) + { + Log.i(TAG, "request setPropBool called, will call native "); + nativeSetPropBool(propBool); + } + + @Override + public StructBoolWithArray getPropBool() + { + Log.i(TAG, "request getPropBool called, will call native "); + return nativeGetPropBool(); + } + + + @Override + public void setPropInt(StructIntWithArray propInt) + { + Log.i(TAG, "request setPropInt called, will call native "); + nativeSetPropInt(propInt); + } + + @Override + public StructIntWithArray getPropInt() + { + Log.i(TAG, "request getPropInt called, will call native "); + return nativeGetPropInt(); + } + + + @Override + public void setPropFloat(StructFloatWithArray propFloat) + { + Log.i(TAG, "request setPropFloat called, will call native "); + nativeSetPropFloat(propFloat); + } + + @Override + public StructFloatWithArray getPropFloat() + { + Log.i(TAG, "request getPropFloat called, will call native "); + return nativeGetPropFloat(); + } + + + @Override + public void setPropString(StructStringWithArray propString) + { + Log.i(TAG, "request setPropString called, will call native "); + nativeSetPropString(propString); + } + + @Override + public StructStringWithArray getPropString() + { + Log.i(TAG, "request getPropString called, will call native "); + return nativeGetPropString(); + } + + + @Override + public void setPropEnum(StructEnumWithArray propEnum) + { + Log.i(TAG, "request setPropEnum called, will call native "); + nativeSetPropEnum(propEnum); + } + + @Override + public StructEnumWithArray getPropEnum() + { + Log.i(TAG, "request getPropEnum called, will call native "); + return nativeGetPropEnum(); + } + + + // methods + + @Override + public StructBool[] funcBool(StructBoolWithArray paramBool) { + Log.w(TAG, "request method funcBool called, will call native"); + return nativeFuncBool(paramBool); + } + + @Override + public CompletableFuture funcBoolAsync(StructBoolWithArray paramBool) { + return CompletableFuture.supplyAsync( + () -> {return funcBool(paramBool); }, + executor); + } + + @Override + public StructInt[] funcInt(StructIntWithArray paramInt) { + Log.w(TAG, "request method funcInt called, will call native"); + return nativeFuncInt(paramInt); + } + + @Override + public CompletableFuture funcIntAsync(StructIntWithArray paramInt) { + return CompletableFuture.supplyAsync( + () -> {return funcInt(paramInt); }, + executor); + } + + @Override + public StructFloat[] funcFloat(StructFloatWithArray paramFloat) { + Log.w(TAG, "request method funcFloat called, will call native"); + return nativeFuncFloat(paramFloat); + } + + @Override + public CompletableFuture funcFloatAsync(StructFloatWithArray paramFloat) { + return CompletableFuture.supplyAsync( + () -> {return funcFloat(paramFloat); }, + executor); + } + + @Override + public StructString[] funcString(StructStringWithArray paramString) { + Log.w(TAG, "request method funcString called, will call native"); + return nativeFuncString(paramString); + } + + @Override + public CompletableFuture funcStringAsync(StructStringWithArray paramString) { + return CompletableFuture.supplyAsync( + () -> {return funcString(paramString); }, + executor); + } + + @Override + public Enum0[] funcEnum(StructEnumWithArray paramEnum) { + Log.w(TAG, "request method funcEnum called, will call native"); + return nativeFuncEnum(paramEnum); + } + + @Override + public CompletableFuture funcEnumAsync(StructEnumWithArray paramEnum) { + return CompletableFuture.supplyAsync( + () -> {return funcEnum(paramEnum); }, + executor); + } + + @Override + public boolean _isReady() { + return isServiceReady; + } + + // Called on Native Impl Service + private native void nativeSetPropBool(StructBoolWithArray propBool); + private native StructBoolWithArray nativeGetPropBool(); + + private native void nativeSetPropInt(StructIntWithArray propInt); + private native StructIntWithArray nativeGetPropInt(); + + private native void nativeSetPropFloat(StructFloatWithArray propFloat); + private native StructFloatWithArray nativeGetPropFloat(); + + private native void nativeSetPropString(StructStringWithArray propString); + private native StructStringWithArray nativeGetPropString(); + + private native void nativeSetPropEnum(StructEnumWithArray propEnum); + private native StructEnumWithArray nativeGetPropEnum(); + + // methods + private native StructBool[] nativeFuncBool(StructBoolWithArray paramBool); + private native StructInt[] nativeFuncInt(StructIntWithArray paramInt); + private native StructFloat[] nativeFuncFloat(StructFloatWithArray paramFloat); + private native StructString[] nativeFuncString(StructStringWithArray paramString); + private native Enum0[] nativeFuncEnum(StructEnumWithArray paramEnum); + + // Called by Native Impl Service + public void nativeServiceReady(boolean value) { + isServiceReady = value; + } + + //In theory event listener interface + public void onPropBoolChanged(StructBoolWithArray newValue) + { + Log.i(TAG, "onPropBoolChanged, will pass notification to all listeners"); + firePropBoolChanged(newValue); + } + public void onPropIntChanged(StructIntWithArray newValue) + { + Log.i(TAG, "onPropIntChanged, will pass notification to all listeners"); + firePropIntChanged(newValue); + } + public void onPropFloatChanged(StructFloatWithArray newValue) + { + Log.i(TAG, "onPropFloatChanged, will pass notification to all listeners"); + firePropFloatChanged(newValue); + } + public void onPropStringChanged(StructStringWithArray newValue) + { + Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); + firePropStringChanged(newValue); + } + public void onPropEnumChanged(StructEnumWithArray newValue) + { + Log.i(TAG, "onPropEnumChanged, will pass notification to all listeners"); + firePropEnumChanged(newValue); + } + public void onSigBool(StructBoolWithArray paramBool) + { + Log.i(TAG, "onSigBool, will pass notification to all listeners"); + fireSigBool(paramBool); + } + public void onSigInt(StructIntWithArray paramInt) + { + Log.i(TAG, "onSigInt, will pass notification to all listeners"); + fireSigInt(paramInt); + } + public void onSigFloat(StructFloatWithArray paramFloat) + { + Log.i(TAG, "onSigFloat, will pass notification to all listeners"); + fireSigFloat(paramFloat); + } + public void onSigString(StructStringWithArray paramString) + { + Log.i(TAG, "onSigString, will pass notification to all listeners"); + fireSigString(paramString); + } + +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java new file mode 100644 index 0000000..add0ec1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java @@ -0,0 +1,81 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +package testbed1.testbed1jniservice; + +import testbed1.testbed1_android_service.IStructArray2InterfaceServiceFactory; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_api.AbstractStructArray2Interface; +import testbed1.testbed1jniservice.StructArray2InterfaceJniService; +import android.util.Log; +import android.os.HandlerThread; +import android.os.Looper; + +import androidx.annotation.NonNull; + +/** + * Shared singleton StructArray2InterfaceJniServiceFactory thread for the system. This is a thread for + * StructArray2InterfaceJniServiceFactory connectivity. + * Various connectivity manager objects can use this singleton as a common + * resource for their handlers instead of creating separate threads of their own. + * + * @hide + */ +public class StructArray2InterfaceJniServiceFactory extends HandlerThread implements IStructArray2InterfaceServiceFactory +{ + private StructArray2InterfaceJniService jniService; + + // A class implementing the lazy holder idiom: the unique static instance + // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by + // the language specs) the first time that Singleton is referenced in get() + // or getInstanceLooper(). + + public static StructArray2InterfaceJniServiceFactory get() + { + return Singleton.INSTANCE; + } + + public static Looper getInstanceLooper() + { + return Singleton.INSTANCE.getLooper(); + } + + public void onDestroy() + { + synchronized (this) + { + Log.w("UE", "LIFECYCLE: StructArray2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Singleton.INSTANCE.quit(); + } + } + + public synchronized AbstractStructArray2Interface getServiceInstance() + { + if (jniService == null) + { + jniService = new StructArray2InterfaceJniService(); + Log.d("UE", "LIFECYCLE: EngineFactory::GetInstance(with OBBFilename) - CREATED new StructArray2InterfaceJniService"); + } + + return jniService; + } + + private static class Singleton + { + private static final StructArray2InterfaceJniServiceFactory INSTANCE = createInstance(); + } + + private StructArray2InterfaceJniServiceFactory() + { + super("EngineFactory"); + } + + @NonNull + private static StructArray2InterfaceJniServiceFactory createInstance() + { + Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + + StructArray2InterfaceJniServiceFactory t = new StructArray2InterfaceJniServiceFactory(); + t.start(); + return t; + } +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java new file mode 100644 index 0000000..c13f66d --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java @@ -0,0 +1,42 @@ +package testbed1.testbed1jniservice; + +import android.util.Log; +import android.content.Context; +import android.content.Intent; + +import testbed1.testbed1_api.IStructArray2InterfaceEventListener; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_service.StructArray2InterfaceServiceAdapter; +import testbed1.testbed1jniservice.StructArray2InterfaceJniServiceFactory; + + +//Use this class to manage lifetime of android server with native backend service. +public class StructArray2InterfaceJniServiceStarter { + + static Intent androidService = null; + private static final String TAG = "StructArray2InterfaceJniStarter"; + + + + public static IStructArray2Interface start(Context context) { + stop(context); + androidService = new Intent(context, StructArray2InterfaceServiceAdapter.class); + Log.w(TAG, "starter: created intent"); + context.startService(androidService); + Log.w(TAG, "starter: started intent (service) "); + StructArray2InterfaceJniServiceFactory factory = StructArray2InterfaceJniServiceFactory.get(); + Log.w(TAG, "starter: factory set for StructArray2InterfaceJniServiceFactory"); + return StructArray2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.w(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java index fb26fa6..2b7d5f5 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java @@ -6,11 +6,16 @@ import testbed1.testbed1_api.IStructArrayInterface; import testbed1.testbed1_api.AbstractStructArrayInterface; import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; import testbed1.testbed1_api.StructBool; -import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructBoolParcelable; import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; - +import testbed1.testbed1_android_messenger.StructStringParcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -93,6 +98,21 @@ public StructString[] getPropString() } + @Override + public void setPropEnum(Enum0[] propEnum) + { + Log.i(TAG, "request setPropEnum called, will call native "); + nativeSetPropEnum(propEnum); + } + + @Override + public Enum0[] getPropEnum() + { + Log.i(TAG, "request getPropEnum called, will call native "); + return nativeGetPropEnum(); + } + + // methods @Override @@ -145,6 +165,19 @@ public CompletableFuture funcStringAsync(StructString[] paramSt return CompletableFuture.supplyAsync( () -> {return funcString(paramString); }, executor); + } + + @Override + public Enum0[] funcEnum(Enum0[] paramEnum) { + Log.w(TAG, "request method funcEnum called, will call native"); + return nativeFuncEnum(paramEnum); + } + + @Override + public CompletableFuture funcEnumAsync(Enum0[] paramEnum) { + return CompletableFuture.supplyAsync( + () -> {return funcEnum(paramEnum); }, + executor); } @Override @@ -165,11 +198,15 @@ public boolean _isReady() { private native void nativeSetPropString(StructString[] propString); private native StructString[] nativeGetPropString(); + private native void nativeSetPropEnum(Enum0[] propEnum); + private native Enum0[] nativeGetPropEnum(); + // methods private native StructBool[] nativeFuncBool(StructBool[] paramBool); private native StructInt[] nativeFuncInt(StructInt[] paramInt); private native StructFloat[] nativeFuncFloat(StructFloat[] paramFloat); private native StructString[] nativeFuncString(StructString[] paramString); + private native Enum0[] nativeFuncEnum(Enum0[] paramEnum); // Called by Native Impl Service public void nativeServiceReady(boolean value) { @@ -197,6 +234,11 @@ public void onPropStringChanged(StructString[] newValue) Log.i(TAG, "onPropStringChanged, will pass notification to all listeners"); firePropStringChanged(newValue); } + public void onPropEnumChanged(Enum0[] newValue) + { + Log.i(TAG, "onPropEnumChanged, will pass notification to all listeners"); + firePropEnumChanged(newValue); + } public void onSigBool(StructBool[] paramBool) { Log.i(TAG, "onSigBool, will pass notification to all listeners"); @@ -217,5 +259,10 @@ public void onSigString(StructString[] paramString) Log.i(TAG, "onSigString, will pass notification to all listeners"); fireSigString(paramString); } + public void onSigEnum(Enum0[] paramEnum) + { + Log.i(TAG, "onSigEnum, will pass notification to all listeners"); + fireSigEnum(paramEnum); + } } diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java index 239dee2..319bec1 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java @@ -7,10 +7,13 @@ import testbed1.testbed1_api.AbstractStructInterface; import testbed1.testbed1_api.IStructInterfaceEventListener; import testbed1.testbed1_api.StructBool; -import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructBoolParcelable; import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; - +import testbed1.testbed1_android_messenger.StructStringParcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/testbed1/testbed1serviceexample/build.gradle b/goldenmaster/testbed1/testbed1serviceexample/build.gradle index d2c11e1..7062dea 100644 --- a/goldenmaster/testbed1/testbed1serviceexample/build.gradle +++ b/goldenmaster/testbed1/testbed1serviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "testbed1" +version = "1.0.0" + + android { namespace 'testbed1.testbed1serviceexample' compileSdk 35 diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml b/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml index 6e3e4ee..365fc3d 100644 --- a/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml @@ -27,6 +27,10 @@ android:name="testbed1.testbed1_android_service.StructArrayInterfaceServiceAdapter" android:enabled="true" android:exported="true"> + diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java index b86a772..8ef99cd 100644 --- a/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java @@ -20,10 +20,10 @@ //import message type and parcelabe types import testbed1.testbed1_api.StructBool; import testbed1.testbed1_android_messenger.StructBoolParcelable; -import testbed1.testbed1_api.StructInt; -import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructFloat; import testbed1.testbed1_android_messenger.StructFloatParcelable; +import testbed1.testbed1_api.StructInt; +import testbed1.testbed1_android_messenger.StructIntParcelable; import testbed1.testbed1_api.StructString; import testbed1.testbed1_android_messenger.StructStringParcelable; diff --git a/goldenmaster/testbed2/testbed2_android_client/additions.gradle b/goldenmaster/testbed2/testbed2_android_client/additions.gradle index 6f5a14d..d5b4fff 100644 --- a/goldenmaster/testbed2/testbed2_android_client/additions.gradle +++ b/goldenmaster/testbed2/testbed2_android_client/additions.gradle @@ -8,6 +8,7 @@ android { } } + dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':testbed2_api') diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java index cc59b8f..11423a4 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java @@ -17,26 +17,6 @@ import android.util.Log; //import message type and parcelabe types -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.IManyParamInterfaceEventListener; import testbed2.testbed2_api.IManyParamInterface; @@ -265,6 +245,7 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); + int param1 = data.getInt("param1", 0); onSig1(param1); @@ -273,6 +254,7 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); + int param1 = data.getInt("param1", 0); @@ -283,6 +265,7 @@ public void handleMessage(Message msg) case SIG_Sig3: { Bundle data = msg.getData(); + int param1 = data.getInt("param1", 0); @@ -295,6 +278,7 @@ public void handleMessage(Message msg) case SIG_Sig4: { Bundle data = msg.getData(); + int param1 = data.getInt("param1", 0); @@ -309,6 +293,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -326,6 +311,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -343,6 +329,7 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -360,6 +347,7 @@ public void handleMessage(Message msg) case RPC_Func4Resp: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java index 4910d0d..73d0483 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java @@ -17,26 +17,8 @@ import android.util.Log; //import message type and parcelabe types -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; import testbed2.testbed2_api.INestedStruct1Interface; @@ -200,7 +182,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -225,7 +207,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); onSig1(param1); @@ -234,7 +217,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java index 8e41a12..aa952ee 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java @@ -17,26 +17,10 @@ import android.util.Log; //import message type and parcelabe types -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; import testbed2.testbed2_api.INestedStruct2Interface; @@ -201,8 +185,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -241,7 +224,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); onSig1(param1); @@ -250,8 +234,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -262,7 +246,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -280,8 +265,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java index adb2a9b..855ada3 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java @@ -17,26 +17,12 @@ import android.util.Log; //import message type and parcelabe types -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; import testbed2.testbed2_api.NestedStruct3; import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; import testbed2.testbed2_api.INestedStruct3Interface; @@ -202,9 +188,7 @@ public void handleMessage(Message msg) { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 prop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -257,7 +241,8 @@ public void handleMessage(Message msg) case SIG_Sig1: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); onSig1(param1); @@ -266,8 +251,8 @@ public void handleMessage(Message msg) case SIG_Sig2: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -278,9 +263,8 @@ public void handleMessage(Message msg) case SIG_Sig3: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -293,7 +277,8 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -311,8 +296,8 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -330,9 +315,8 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java index 7bbbdff..b97d485 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java @@ -174,7 +174,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); inOrderEventListener.verify(listenerMock,times(1)).onProp4Changed(testprop4); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -188,7 +187,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(testprop1); } - + @Test public void setPropertyRequestprop1() { @@ -196,7 +195,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -206,7 +204,7 @@ public void setPropertyRequestprop1() int receivedprop1 = data.getInt("prop1", 0); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -220,7 +218,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(testprop2); } - + @Test public void setPropertyRequestprop2() { @@ -228,7 +226,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -238,7 +235,7 @@ public void setPropertyRequestprop2() int receivedprop2 = data.getInt("prop2", 0); assertEquals(receivedprop2, testprop2); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop3PropertyChangeTest() throws RemoteException { // Create and send message @@ -252,7 +249,7 @@ public void onReceiveprop3PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(testprop3); } - + @Test public void setPropertyRequestprop3() { @@ -260,7 +257,6 @@ public void setPropertyRequestprop3() testedClient.setProp3(testprop3); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -270,7 +266,7 @@ public void setPropertyRequestprop3() int receivedprop3 = data.getInt("prop3", 0); assertEquals(receivedprop3, testprop3); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop4PropertyChangeTest() throws RemoteException { // Create and send message @@ -284,7 +280,7 @@ public void onReceiveprop4PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp4Changed(testprop4); } - + @Test public void setPropertyRequestprop4() { @@ -292,7 +288,6 @@ public void setPropertyRequestprop4() testedClient.setProp4(testprop4); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -302,6 +297,7 @@ public void setPropertyRequestprop4() int receivedprop4 = data.getInt("prop4", 0); assertEquals(receivedprop4, testprop4); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -403,6 +399,7 @@ public void onfunc1Request() throws RemoteException { assertEquals(ManyParamInterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); int returnedCallId = data.getInt("callId", -1); @@ -448,6 +445,7 @@ public void onfunc2Request() throws RemoteException { assertEquals(ManyParamInterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); @@ -497,6 +495,7 @@ public void onfunc3Request() throws RemoteException { assertEquals(ManyParamInterfaceMessageType.RPC_Func3Req.getValue(), method_request.what); Bundle data = method_request.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); @@ -550,6 +549,7 @@ public void onfunc4Request() throws RemoteException { assertEquals(ManyParamInterfaceMessageType.RPC_Func4Req.getValue(), method_request.what); Bundle data = method_request.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java index 3b7ec74..73898bc 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java @@ -165,7 +165,6 @@ public void onInitReceive() throws RemoteException Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -179,7 +178,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); } - + @Test public void setPropertyRequestprop1() { @@ -187,7 +186,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -198,6 +196,7 @@ public void setPropertyRequestprop1() NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedprop1, testprop1); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -238,7 +237,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NestedStruct1InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java index 7fadcef..9e2f8a9 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java @@ -168,7 +168,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -182,7 +181,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); } - + @Test public void setPropertyRequestprop1() { @@ -190,7 +189,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -201,7 +199,7 @@ public void setPropertyRequestprop1() NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -215,7 +213,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); } - + @Test public void setPropertyRequestprop2() { @@ -223,7 +221,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -234,6 +231,7 @@ public void setPropertyRequestprop2() NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); assertEquals(receivedprop2, testprop2); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -292,7 +290,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NestedStruct2InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); @@ -338,8 +337,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NestedStruct2InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java index 8baaf0b..1e17fdf 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java @@ -171,7 +171,6 @@ public void onInitReceive() throws RemoteException inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(any(NestedStruct3.class)); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -185,7 +184,7 @@ public void onReceiveprop1PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp1Changed(any(NestedStruct1.class)); } - + @Test public void setPropertyRequestprop1() { @@ -193,7 +192,6 @@ public void setPropertyRequestprop1() testedClient.setProp1(testprop1); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -204,7 +202,7 @@ public void setPropertyRequestprop1() NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -218,7 +216,7 @@ public void onReceiveprop2PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp2Changed(any(NestedStruct2.class)); } - + @Test public void setPropertyRequestprop2() { @@ -226,7 +224,6 @@ public void setPropertyRequestprop2() testedClient.setProp2(testprop2); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -237,7 +234,7 @@ public void setPropertyRequestprop2() NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); assertEquals(receivedprop2, testprop2); } -//TODO do not add when a property is readonly + @Test public void onReceiveprop3PropertyChangeTest() throws RemoteException { // Create and send message @@ -251,7 +248,7 @@ public void onReceiveprop3PropertyChangeTest() throws RemoteException { Robolectric.flushForegroundThreadScheduler(); inOrderEventListener.verify(listenerMock,times(1)).onProp3Changed(any(NestedStruct3.class)); } - + @Test public void setPropertyRequestprop3() { @@ -259,7 +256,6 @@ public void setPropertyRequestprop3() testedClient.setProp3(testprop3); Robolectric.flushForegroundThreadScheduler(); - inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); Message response = messageCaptor.getValue(); @@ -270,6 +266,7 @@ public void setPropertyRequestprop3() NestedStruct3 receivedprop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); assertEquals(receivedprop3, testprop3); } + @Test public void whenNotifiedsig1() throws RemoteException { @@ -348,7 +345,8 @@ public void onfunc1Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NestedStruct3InterfaceMessageType.RPC_Func1Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); @@ -394,8 +392,8 @@ public void onfunc2Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NestedStruct3InterfaceMessageType.RPC_Func2Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); @@ -445,9 +443,8 @@ public void onfunc3Request() throws RemoteException { Message method_request = messageCaptor.getValue(); assertEquals(NestedStruct3InterfaceMessageType.RPC_Func3Req.getValue(), method_request.what); Bundle data = method_request.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceParcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceParcelable.java new file mode 100644 index 0000000..271f2f1 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/ManyParamInterfaceParcelable.java @@ -0,0 +1,70 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.IManyParamInterface; +import android.os.Parcel; +import android.os.Parcelable; + + public class ManyParamInterfaceParcelable implements Parcelable { + + public IManyParamInterface data; + + public ManyParamInterfaceParcelable(IManyParamInterface data) { + this.data = data; + } + + public IManyParamInterface getManyParamInterface() + { + return data; + } + + protected ManyParamInterfaceParcelable(Parcel in) { + data.setProp1(in.readInt()); + data.setProp2(in.readInt()); + data.setProp3(in.readInt()); + data.setProp4(in.readInt()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public ManyParamInterfaceParcelable createFromParcel(Parcel in) { + return new ManyParamInterfaceParcelable(in); + } + + @Override + public ManyParamInterfaceParcelable[] newArray(int size) { + return new ManyParamInterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.getProp1()); + dest.writeInt(data.getProp2()); + dest.writeInt(data.getProp3()); + dest.writeInt(data.getProp4()); + + + } + public static ManyParamInterfaceParcelable[] wrapArray(IManyParamInterface[] elements) { + if (elements == null) return null; + ManyParamInterfaceParcelable[] out = new ManyParamInterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new ManyParamInterfaceParcelable(elements[i]); + } + return out; + } + + public static IManyParamInterface[] unwrapArray(ManyParamInterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + IManyParamInterface[] out = new IManyParamInterface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getManyParamInterface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceParcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceParcelable.java new file mode 100644 index 0000000..772c438 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceParcelable.java @@ -0,0 +1,66 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.INestedStruct1Interface; +import android.os.Parcel; +import android.os.Parcelable; +import testbed2.testbed2_api.NestedStruct1; + + public class NestedStruct1InterfaceParcelable implements Parcelable { + + public INestedStruct1Interface data; + + public NestedStruct1InterfaceParcelable(INestedStruct1Interface data) { + this.data = data; + } + + public INestedStruct1Interface getNestedStruct1Interface() + { + return data; + } + + protected NestedStruct1InterfaceParcelable(Parcel in) { + NestedStruct1Parcelable l_parcelableprop1 = in.readParcelable(NestedStruct1Parcelable.class.getClassLoader(), NestedStruct1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NestedStruct1InterfaceParcelable createFromParcel(Parcel in) { + return new NestedStruct1InterfaceParcelable(in); + } + + @Override + public NestedStruct1InterfaceParcelable[] newArray(int size) { + return new NestedStruct1InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new NestedStruct1Parcelable(data.getProp1()), flags); + + + } + public static NestedStruct1InterfaceParcelable[] wrapArray(INestedStruct1Interface[] elements) { + if (elements == null) return null; + NestedStruct1InterfaceParcelable[] out = new NestedStruct1InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NestedStruct1InterfaceParcelable(elements[i]); + } + return out; + } + + public static INestedStruct1Interface[] unwrapArray(NestedStruct1InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + INestedStruct1Interface[] out = new INestedStruct1Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNestedStruct1Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceParcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceParcelable.java new file mode 100644 index 0000000..5bb3a73 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct2InterfaceParcelable.java @@ -0,0 +1,70 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.INestedStruct2Interface; +import android.os.Parcel; +import android.os.Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; + + public class NestedStruct2InterfaceParcelable implements Parcelable { + + public INestedStruct2Interface data; + + public NestedStruct2InterfaceParcelable(INestedStruct2Interface data) { + this.data = data; + } + + public INestedStruct2Interface getNestedStruct2Interface() + { + return data; + } + + protected NestedStruct2InterfaceParcelable(Parcel in) { + NestedStruct1Parcelable l_parcelableprop1 = in.readParcelable(NestedStruct1Parcelable.class.getClassLoader(), NestedStruct1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + NestedStruct2Parcelable l_parcelableprop2 = in.readParcelable(NestedStruct2Parcelable.class.getClassLoader(), NestedStruct2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NestedStruct2InterfaceParcelable createFromParcel(Parcel in) { + return new NestedStruct2InterfaceParcelable(in); + } + + @Override + public NestedStruct2InterfaceParcelable[] newArray(int size) { + return new NestedStruct2InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new NestedStruct1Parcelable(data.getProp1()), flags); + dest.writeParcelable(new NestedStruct2Parcelable(data.getProp2()), flags); + + + } + public static NestedStruct2InterfaceParcelable[] wrapArray(INestedStruct2Interface[] elements) { + if (elements == null) return null; + NestedStruct2InterfaceParcelable[] out = new NestedStruct2InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NestedStruct2InterfaceParcelable(elements[i]); + } + return out; + } + + public static INestedStruct2Interface[] unwrapArray(NestedStruct2InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + INestedStruct2Interface[] out = new INestedStruct2Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNestedStruct2Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceParcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceParcelable.java new file mode 100644 index 0000000..26a5ed9 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3InterfaceParcelable.java @@ -0,0 +1,74 @@ +package testbed2.testbed2_android_messenger; + +import testbed2.testbed2_api.INestedStruct3Interface; +import android.os.Parcel; +import android.os.Parcelable; +import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; + + public class NestedStruct3InterfaceParcelable implements Parcelable { + + public INestedStruct3Interface data; + + public NestedStruct3InterfaceParcelable(INestedStruct3Interface data) { + this.data = data; + } + + public INestedStruct3Interface getNestedStruct3Interface() + { + return data; + } + + protected NestedStruct3InterfaceParcelable(Parcel in) { + NestedStruct1Parcelable l_parcelableprop1 = in.readParcelable(NestedStruct1Parcelable.class.getClassLoader(), NestedStruct1Parcelable.class); + data.setProp1(l_parcelableprop1 != null ? l_parcelableprop1.data : null); + NestedStruct2Parcelable l_parcelableprop2 = in.readParcelable(NestedStruct2Parcelable.class.getClassLoader(), NestedStruct2Parcelable.class); + data.setProp2(l_parcelableprop2 != null ? l_parcelableprop2.data : null); + NestedStruct3Parcelable l_parcelableprop3 = in.readParcelable(NestedStruct3Parcelable.class.getClassLoader(), NestedStruct3Parcelable.class); + data.setProp3(l_parcelableprop3 != null ? l_parcelableprop3.data : null); + } + + public static final Creator CREATOR = new Creator() { + @Override + public NestedStruct3InterfaceParcelable createFromParcel(Parcel in) { + return new NestedStruct3InterfaceParcelable(in); + } + + @Override + public NestedStruct3InterfaceParcelable[] newArray(int size) { + return new NestedStruct3InterfaceParcelable[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(new NestedStruct1Parcelable(data.getProp1()), flags); + dest.writeParcelable(new NestedStruct2Parcelable(data.getProp2()), flags); + dest.writeParcelable(new NestedStruct3Parcelable(data.getProp3()), flags); + + + } + public static NestedStruct3InterfaceParcelable[] wrapArray(INestedStruct3Interface[] elements) { + if (elements == null) return null; + NestedStruct3InterfaceParcelable[] out = new NestedStruct3InterfaceParcelable[elements.length]; + for (int i = 0; i < elements.length; i++) { + out[i] = new NestedStruct3InterfaceParcelable(elements[i]); + } + return out; + } + + public static INestedStruct3Interface[] unwrapArray(NestedStruct3InterfaceParcelable[] parcelables) { + if (parcelables == null) return null; + INestedStruct3Interface[] out = new INestedStruct3Interface[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + out[i] = parcelables[i].getNestedStruct3Interface(); + } + return out; + } + + @Override + public int describeContents() { + return 0; + } + } diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java index 83aca37..ae7d8f6 100644 --- a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java @@ -3,6 +3,7 @@ import testbed2.testbed2_api.NestedStruct3; import android.os.Parcel; import android.os.Parcelable; +import testbed2.testbed2_api.Struct1; import testbed2.testbed2_api.Struct2; import testbed2.testbed2_api.Struct3; @@ -21,18 +22,12 @@ public NestedStruct3 getNestedStruct3() protected NestedStruct3Parcelable(Parcel in) { this.data = new NestedStruct3(); - Enum1Parcelable l_parcelablefield1 = in.readParcelable(Enum1Parcelable.class.getClassLoader(), Enum1Parcelable.class); + Struct1Parcelable l_parcelablefield1 = in.readParcelable(Struct1Parcelable.class.getClassLoader(), Struct1Parcelable.class); data.field1 = l_parcelablefield1 != null ? l_parcelablefield1.data : null; Struct2Parcelable l_parcelablefield2 = in.readParcelable(Struct2Parcelable.class.getClassLoader(), Struct2Parcelable.class); data.field2 = l_parcelablefield2 != null ? l_parcelablefield2.data : null; Struct3Parcelable l_parcelablefield3 = in.readParcelable(Struct3Parcelable.class.getClassLoader(), Struct3Parcelable.class); data.field3 = l_parcelablefield3 != null ? l_parcelablefield3.data : null; - Enum1Parcelable[] l_parcelablefieldX = in.createTypedArray(Enum1Parcelable.CREATOR); - data.fieldX = Enum1Parcelable.unwrapArray(l_parcelablefieldX); - Struct2Parcelable[] l_parcelablefieldY = in.createTypedArray(Struct2Parcelable.CREATOR); - data.fieldY = Struct2Parcelable.unwrapArray(l_parcelablefieldY); - Struct3Parcelable[] l_parcelablefieldZ = in.createTypedArray(Struct3Parcelable.CREATOR); - data.fieldZ = Struct3Parcelable.unwrapArray(l_parcelablefieldZ); } public static final Creator CREATOR = new Creator() { @@ -49,12 +44,9 @@ public NestedStruct3Parcelable[] newArray(int size) { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(new Enum1Parcelable(data.field1), flags); + dest.writeParcelable(new Struct1Parcelable(data.field1), flags); dest.writeParcelable(new Struct2Parcelable(data.field2), flags); dest.writeParcelable(new Struct3Parcelable(data.field3), flags); - dest.writeTypedArray(Enum1Parcelable.wrapArray(data.fieldX), flags); - dest.writeTypedArray(Struct2Parcelable.wrapArray(data.fieldY), flags); - dest.writeTypedArray(Struct3Parcelable.wrapArray(data.fieldZ), flags); } diff --git a/goldenmaster/testbed2/testbed2_android_service/additions.gradle b/goldenmaster/testbed2/testbed2_android_service/additions.gradle index 18e9927..7235574 100644 --- a/goldenmaster/testbed2/testbed2_android_service/additions.gradle +++ b/goldenmaster/testbed2/testbed2_android_service/additions.gradle @@ -11,8 +11,8 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':testbed2_api') - implementation project(':testbed2_impl') - implementation project(':testbed2_android_messenger') + api project(':testbed2_impl') + api project(':testbed2_android_messenger') testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' diff --git a/goldenmaster/testbed2/testbed2_android_service/build.gradle b/goldenmaster/testbed2/testbed2_android_service/build.gradle index cb34023..a2ca560 100644 --- a/goldenmaster/testbed2/testbed2_android_service/build.gradle +++ b/goldenmaster/testbed2/testbed2_android_service/build.gradle @@ -5,7 +5,6 @@ plugins { group = "testbed2" version = "1.0.0" - android { namespace 'testbed2.testbed2_android_service' compileSdk 35 diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java index f505294..40cc5ae 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java @@ -14,26 +14,6 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.IManyParamInterfaceEventListener; import testbed2.testbed2_android_service.IManyParamInterfaceServiceFactory; @@ -49,10 +29,11 @@ public class ManyParamInterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static IManyParamInterface mBackendService; private static IManyParamInterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -67,10 +48,19 @@ public static IManyParamInterface setService(IManyParamInterfaceServiceFactory f { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(ManyParamInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -81,13 +71,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(ManyParamInterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -104,18 +106,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(ManyParamInterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(ManyParamInterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(ManyParamInterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -235,6 +240,7 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int param1 = data.getInt("param1", 0); @@ -263,6 +269,7 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int param1 = data.getInt("param1", 0); @@ -293,6 +300,7 @@ public void handleMessage(Message msg) case RPC_Func3Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int param1 = data.getInt("param1", 0); @@ -325,6 +333,7 @@ public void handleMessage(Message msg) case RPC_Func4Req: { Bundle data = msg.getData(); + int callId = data.getInt("callId"); int param1 = data.getInt("param1", 0); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java index e816caa..8a53d89 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java @@ -14,26 +14,8 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; import testbed2.testbed2_android_service.INestedStruct1InterfaceServiceFactory; @@ -49,10 +31,11 @@ public class NestedStruct1InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INestedStruct1Interface mBackendService; private static INestedStruct1InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -67,10 +50,19 @@ public static INestedStruct1Interface setService(INestedStruct1InterfaceServiceF { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NestedStruct1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -81,13 +73,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct1InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -104,18 +108,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct1InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct1InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -212,7 +219,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java index e7f5ac7..5befe8c 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java @@ -14,26 +14,10 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; import testbed2.testbed2_android_service.INestedStruct2InterfaceServiceFactory; @@ -49,10 +33,11 @@ public class NestedStruct2InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INestedStruct2Interface mBackendService; private static INestedStruct2InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -67,10 +52,19 @@ public static INestedStruct2Interface setService(INestedStruct2InterfaceServiceF { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NestedStruct2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -81,13 +75,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct2InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -104,18 +110,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct2InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct2InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -221,7 +230,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -250,8 +260,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java index d6656f0..7ff0f3f 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java @@ -14,26 +14,12 @@ import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; import testbed2.testbed2_api.NestedStruct3; import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; import testbed2.testbed2_android_service.INestedStruct3InterfaceServiceFactory; @@ -49,10 +35,11 @@ public class NestedStruct3InterfaceServiceAdapter extends Service /** * Target we publish for clients to send messages to IncomingHandler. */ - private Messenger mMessenger; + private static Messenger mMessenger; private static IncomingHandler mHandler = null; private static INestedStruct3Interface mBackendService; private static INestedStruct3InterfaceServiceFactory mServiceFactory; + private static final Object mutex = new Object(); //private final List mMessagesQueue = new ArrayList<>(); @@ -67,10 +54,19 @@ public static INestedStruct3Interface setService(INestedStruct3InterfaceServiceF { mServiceFactory = factory; } - mBackendService = mServiceFactory.getServiceInstance(); - if (mHandler != null) + synchronized (mutex) { - mBackendService.addEventListener(mHandler); + if (mHandler != null && mBackendService != null) + { + // remove old event listener (backend is about to change) + mBackendService.removeEventListener(mHandler); + } + mBackendService = mServiceFactory.getServiceInstance(); + if (mHandler != null) + { + Log.i(TAG, "LIFECYCLE: setService(NestedStruct3Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } return mBackendService; } @@ -81,13 +77,25 @@ public void onCreate() { super.onCreate(); Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct3InterfaceService) called. context = " + this); - - mHandler = new IncomingHandler(this); - mMessenger = new Messenger(mHandler); - if (mBackendService != null) - { - mBackendService.addEventListener(mHandler); + synchronized (mutex) { + if (mHandler != null && mBackendService != null) + { + // The handler (event listener) is about to change. + mBackendService.removeEventListener(mHandler); + } + if (mHandler != null) + { + mHandler.removeCallbacksAndMessages(null); + } + mHandler = new IncomingHandler(this); + mMessenger = new Messenger(mHandler); + if (mBackendService != null) + { + Log.i(TAG, "LIFECYCLE: Add event listern to a backend called for handler " + mHandler); + mBackendService.addEventListener(mHandler); + } } + } // execution of service will start on calling this method @@ -104,18 +112,21 @@ public int onStartCommand(Intent intent, int flags, int startId) @Override public void onDestroy() { - super.onDestroy(); - - Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct3InterfaceService) - proc = " + ", mMessenger = " + mMessenger - ); + Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct3InterfaceService) - proc = " + ", mMessenger = " + mMessenger); - if (mBackendService != null) + if (mHandler != null) { - Log.i(TAG, "LIFECYCLE: onDestroy(NestedStruct3InterfaceService) - proc = " + ", remove engine event callback!"); - - mBackendService.removeEventListener(mHandler); - mBackendService = null; + if (mBackendService != null) + { + mBackendService.removeEventListener(mHandler); + } + mHandler.removeCallbacksAndMessages(null); + mHandler = null; } + mBackendService = null; + mMessenger = null; + + super.onDestroy(); } @Override @@ -230,7 +241,8 @@ public void handleMessage(Message msg) case RPC_Func1Req: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -259,8 +271,8 @@ public void handleMessage(Message msg) case RPC_Func2Req: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); @@ -291,9 +303,8 @@ public void handleMessage(Message msg) case RPC_Func3Req: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java index 896d670..eddc220 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java @@ -192,6 +192,7 @@ void registerFakeActivityClient(Messenger messenger, String id) int receivedprop3 = data.getInt("prop3", 0); int receivedprop4 = data.getInt("prop4", 0); + assertEquals(receivedprop1, initprop1); assertEquals(receivedprop2, initprop2); assertEquals(receivedprop3, initprop3); @@ -233,7 +234,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -268,7 +268,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -303,7 +302,6 @@ public void whenNotifiedprop2() assertEquals(receivedprop2, testprop2); } -//TODO do not add when a property is readonly @Test public void onReceiveprop3PropertyChangeTest() throws RemoteException { // Create and send message @@ -338,7 +336,6 @@ public void whenNotifiedprop3() assertEquals(receivedprop3, testprop3); } -//TODO do not add when a property is readonly @Test public void onReceiveprop4PropertyChangeTest() throws RemoteException { // Create and send message @@ -387,6 +384,7 @@ public void whenNotifiedsig1() assertEquals(ManyParamInterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); } @@ -405,6 +403,7 @@ public void whenNotifiedsig2() assertEquals(ManyParamInterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); @@ -427,6 +426,7 @@ public void whenNotifiedsig3() assertEquals(ManyParamInterfaceMessageType.SIG_Sig3.getValue(), response.what); Bundle data = response.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); @@ -453,6 +453,7 @@ public void whenNotifiedsig4() assertEquals(ManyParamInterfaceMessageType.SIG_Sig4.getValue(), response.what); Bundle data = response.getData(); + int receivedparam1 = data.getInt("param1", 0); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java index 5b3b865..c54a3a7 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java @@ -178,7 +178,8 @@ void registerFakeActivityClient(Messenger messenger, String id) Bundle data = response.getData(); NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); } @@ -217,7 +218,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -265,7 +265,8 @@ public void whenNotifiedsig1() assertEquals(NestedStruct1InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java index afc88de..800ed2e 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java @@ -184,8 +184,8 @@ void registerFakeActivityClient(Messenger messenger, String id) NestedStruct1 receivedprop1 = data.getParcelable("prop1", NestedStruct1Parcelable.class).getNestedStruct1(); NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); // assertEquals(receivedprop2, initprop2); @@ -225,7 +225,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -260,7 +259,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -308,7 +306,8 @@ public void whenNotifiedsig1() assertEquals(NestedStruct2InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); @@ -327,8 +326,8 @@ public void whenNotifiedsig2() assertEquals(NestedStruct2InterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java index 1cc01c4..dda8c61 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java @@ -190,9 +190,8 @@ void registerFakeActivityClient(Messenger messenger, String id) NestedStruct2 receivedprop2 = data.getParcelable("prop2", NestedStruct2Parcelable.class).getNestedStruct2(); NestedStruct3 receivedprop3 = data.getParcelable("prop3", NestedStruct3Parcelable.class).getNestedStruct3(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); // assertEquals(receivedprop1, initprop1); // assertEquals(receivedprop2, initprop2); // assertEquals(receivedprop3, initprop3); @@ -233,7 +232,6 @@ public void setUp() throws RemoteException // All emitted signals and property changes are forwarded to it. registerFakeActivityClient(clientReplyMessenger, mTestConnectionID1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop1PropertyChangeTest() throws RemoteException { // Create and send message @@ -268,7 +266,6 @@ public void whenNotifiedprop1() assertEquals(receivedprop1, testprop1); } -//TODO do not add when a property is readonly @Test public void onReceiveprop2PropertyChangeTest() throws RemoteException { // Create and send message @@ -303,7 +300,6 @@ public void whenNotifiedprop2() assertEquals(receivedprop2, testprop2); } -//TODO do not add when a property is readonly @Test public void onReceiveprop3PropertyChangeTest() throws RemoteException { // Create and send message @@ -351,7 +347,8 @@ public void whenNotifiedsig1() assertEquals(NestedStruct3InterfaceMessageType.SIG_Sig1.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); @@ -370,8 +367,8 @@ public void whenNotifiedsig2() assertEquals(NestedStruct3InterfaceMessageType.SIG_Sig2.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); @@ -394,9 +391,8 @@ public void whenNotifiedsig3() assertEquals(NestedStruct3InterfaceMessageType.SIG_Sig3.getValue(), response.what); Bundle data = response.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct2Parcelable.class.getClassLoader()); - data.setClassLoader(NestedStruct3Parcelable.class.getClassLoader()); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); assertEquals(receivedparam1, testparam1); diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java index ac66073..53aeb1a 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java @@ -2,7 +2,6 @@ import testbed2.testbed2_api.IManyParamInterfaceEventListener; import testbed2.testbed2_api.IManyParamInterface; -//TODO imported/extern modules import testbed2.testbed2_api.Struct1; import testbed2.testbed2_api.Struct2; import testbed2.testbed2_api.Struct3; diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java index 8051ed8..1706e01 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java @@ -2,7 +2,6 @@ import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; import testbed2.testbed2_api.INestedStruct1Interface; -//TODO imported/extern modules import testbed2.testbed2_api.Struct1; import testbed2.testbed2_api.Struct2; import testbed2.testbed2_api.Struct3; diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java index 05e271d..846410c 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java @@ -2,7 +2,6 @@ import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; import testbed2.testbed2_api.INestedStruct2Interface; -//TODO imported/extern modules import testbed2.testbed2_api.Struct1; import testbed2.testbed2_api.Struct2; import testbed2.testbed2_api.Struct3; diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java index 7339b4a..cc23432 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java @@ -2,7 +2,6 @@ import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; import testbed2.testbed2_api.INestedStruct3Interface; -//TODO imported/extern modules import testbed2.testbed2_api.Struct1; import testbed2.testbed2_api.Struct2; import testbed2.testbed2_api.Struct3; diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java index 842aa63..1bdc40f 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java @@ -6,54 +6,31 @@ public class NestedStruct3 { - public NestedStruct3(Enum1 field1, Struct2 field2, Struct3 field3, Enum1[] fieldX, Struct2[] fieldY, Struct3[] fieldZ) + public NestedStruct3(Struct1 field1, Struct2 field2, Struct3 field3) { this.field1 = field1; this.field2 = field2; this.field3 = field3; - this.fieldX = fieldX; - this.fieldY = fieldY; - this.fieldZ = fieldZ; } public NestedStruct3() { - this.field1 = Enum1.values()[0]; + this.field1 = new Struct1(); this.field2 = new Struct2(); this.field3 = new Struct3(); - this.fieldX = new Enum1[0]; - this.fieldY = new Struct2[0]; - this.fieldZ = new Struct3[0]; } @JsonProperty("field1") - public Enum1 field1; + public Struct1 field1; @JsonProperty("field2") public Struct2 field2; @JsonProperty("field3") public Struct3 field3; - @JsonProperty("field_x") - public Enum1[] fieldX; - @JsonProperty("field_y") - public Struct2[] fieldY; - @JsonProperty("field_z") - public Struct3[] fieldZ; public NestedStruct3(NestedStruct3 other) { - this.field1 = other.field1; + this.field1 = new Struct1(other.field1); this.field2 = new Struct2(other.field2); this.field3 = new Struct3(other.field3); - this.fieldX = java.util.Arrays.copyOf(other.fieldX, other.fieldX.length); - this.fieldY = new Struct2[other.fieldY.length]; - for (int i = 0; i < other.fieldY.length; i++) - { - this.fieldY[i] = new Struct2(other.fieldY[i]); - } - this.fieldZ = new Struct3[other.fieldZ.length]; - for (int i = 0; i < other.fieldZ.length; i++) - { - this.fieldZ[i] = new Struct3(other.fieldZ[i]); - } } @Override @@ -63,12 +40,9 @@ public boolean equals(Object o) { NestedStruct3 other = (NestedStruct3) o; return - this.field1 == other.field1 + Objects.equals(this.field1, other.field1) && Objects.equals(this.field2, other.field2) - && Objects.equals(this.field3, other.field3) - && Arrays.equals(this.fieldX, other.fieldX) - && Arrays.equals(this.fieldY, other.fieldY) - && Arrays.equals(this.fieldZ, other.fieldZ); + && Objects.equals(this.field3, other.field3); } @Override @@ -77,9 +51,6 @@ public int hashCode() { result = 31 * result + Objects.hashCode(field1); result = 31 * result + Objects.hashCode(field2); result = 31 * result + Objects.hashCode(field3); - result = 31 * result + Arrays.hashCode(fieldX); - result = 31 * result + Arrays.hashCode(fieldY); - result = 31 * result + Arrays.hashCode(fieldZ); return result; } diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java index f62ad6e..26c897c 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Arrays; - public class Testbed2TestHelper { @@ -45,31 +44,64 @@ static public Struct4 makeTestStruct4() static public NestedStruct1 makeTestNestedStruct1() { NestedStruct1 testStruct = new NestedStruct1(); - testStruct.field1 = makeTestStruct1(); + testStruct.field1 = Testbed2TestHelper.makeTestStruct1(); return testStruct; } static public NestedStruct2 makeTestNestedStruct2() { NestedStruct2 testStruct = new NestedStruct2(); - testStruct.field1 = makeTestStruct1(); - testStruct.field2 = makeTestStruct2(); + testStruct.field1 = Testbed2TestHelper.makeTestStruct1(); + testStruct.field2 = Testbed2TestHelper.makeTestStruct2(); return testStruct; } static public NestedStruct3 makeTestNestedStruct3() { NestedStruct3 testStruct = new NestedStruct3(); - testStruct.field1 = Enum1.Value2; - testStruct.field2 = makeTestStruct2(); - testStruct.field3 = makeTestStruct3(); - testStruct.fieldX = new Enum1[1]; - testStruct.fieldX[0] = Enum1.Value2; - testStruct.fieldY = new Struct2[1]; - testStruct.fieldY[0] = makeTestStruct2(); - testStruct.fieldZ = new Struct3[1]; - testStruct.fieldZ[0] = makeTestStruct3(); + testStruct.field1 = Testbed2TestHelper.makeTestStruct1(); + testStruct.field2 = Testbed2TestHelper.makeTestStruct2(); + testStruct.field3 = Testbed2TestHelper.makeTestStruct3(); return testStruct; } + static public IManyParamInterface makeTestManyParamInterface(IManyParamInterface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + testObjToFill.setProp1(1); + testObjToFill.setProp2(1); + testObjToFill.setProp3(1); + testObjToFill.setProp4(1); + return testObjToFill; + } + + static public INestedStruct1Interface makeTestNestedStruct1Interface(INestedStruct1Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + NestedStruct1 localprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + testObjToFill.setProp1(localprop1); + return testObjToFill; + } + + static public INestedStruct2Interface makeTestNestedStruct2Interface(INestedStruct2Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + NestedStruct1 localprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + testObjToFill.setProp1(localprop1); + NestedStruct2 localprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + testObjToFill.setProp2(localprop2); + return testObjToFill; + } + + static public INestedStruct3Interface makeTestNestedStruct3Interface(INestedStruct3Interface testObjToFill) + { + if (testObjToFill == null){return testObjToFill;} + NestedStruct1 localprop1 = Testbed2TestHelper.makeTestNestedStruct1(); + testObjToFill.setProp1(localprop1); + NestedStruct2 localprop2 = Testbed2TestHelper.makeTestNestedStruct2(); + testObjToFill.setProp2(localprop2); + NestedStruct3 localprop3 = Testbed2TestHelper.makeTestNestedStruct3(); + testObjToFill.setProp3(localprop3); + return testObjToFill; + } } \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_client_example/build.gradle b/goldenmaster/testbed2/testbed2_client_example/build.gradle index 2eeada1..3b58c0a 100644 --- a/goldenmaster/testbed2/testbed2_client_example/build.gradle +++ b/goldenmaster/testbed2/testbed2_client_example/build.gradle @@ -2,6 +2,9 @@ plugins { alias(libs.plugins.android.application) } +group = "testbed2" +version = "1.0.0" + android { namespace 'testbed2.testbed2_client_example' compileSdk 35 diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java index 5bd796d..1e4b5e9 100644 --- a/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java @@ -13,29 +13,6 @@ //TODO for each interface there coudl be a tab? now only first one is added import testbed2.testbed2_android_client.ManyParamInterfaceClient; - -//import message type and parcelabe types -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; - import testbed2.testbed2_api.IManyParamInterfaceEventListener; import java.util.concurrent.CompletableFuture; @@ -373,4 +350,4 @@ public void on_readyStatusChanged(boolean isReady) } } -} +} \ No newline at end of file diff --git a/goldenmaster/testbed2/testbed2_impl/additions.gradle b/goldenmaster/testbed2/testbed2_impl/additions.gradle index 1f4ebbd..776b335 100644 --- a/goldenmaster/testbed2/testbed2_impl/additions.gradle +++ b/goldenmaster/testbed2/testbed2_impl/additions.gradle @@ -1,3 +1,4 @@ + android { namespace 'testbed2.testbed2_impl' diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java index 177ce59..a64092c 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java @@ -6,16 +6,6 @@ import testbed2.testbed2_api.IManyParamInterface; import testbed2.testbed2_api.AbstractManyParamInterface; import testbed2.testbed2_api.IManyParamInterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; import java.util.Map; diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java index c38396a..88b904d 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java @@ -6,16 +6,7 @@ import testbed2.testbed2_api.INestedStruct1Interface; import testbed2.testbed2_api.AbstractNestedStruct1Interface; import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; import java.util.Map; diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java index c966dd1..1afd134 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java @@ -6,16 +6,8 @@ import testbed2.testbed2_api.INestedStruct2Interface; import testbed2.testbed2_api.AbstractNestedStruct2Interface; import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; import java.util.Map; diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java index 055bfd0..a425581 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java @@ -6,16 +6,9 @@ import testbed2.testbed2_api.INestedStruct3Interface; import testbed2.testbed2_api.AbstractNestedStruct3Interface; import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; import testbed2.testbed2_api.NestedStruct2; import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; import java.util.Map; diff --git a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java index b836a46..24c86f4 100644 --- a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java @@ -5,16 +5,6 @@ import testbed2.testbed2_api.IManyParamInterfaceEventListener; import testbed2.testbed2_android_client.ManyParamInterfaceClient; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java index 06934cc..2924717 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -5,16 +5,8 @@ import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; import testbed2.testbed2_android_client.NestedStruct1InterfaceClient; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java index bc42e80..d06d858 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java @@ -5,16 +5,10 @@ import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; import testbed2.testbed2_android_client.NestedStruct2InterfaceClient; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java index 6118486..62c519f 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java @@ -5,16 +5,12 @@ import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; import testbed2.testbed2_android_client.NestedStruct3InterfaceClient; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; import android.content.Context; import android.os.Bundle; diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java index ea89381..7f8a00e 100644 --- a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java @@ -6,17 +6,6 @@ import testbed2.testbed2_api.IManyParamInterface; import testbed2.testbed2_api.AbstractManyParamInterface; import testbed2.testbed2_api.IManyParamInterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; - import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java index 090da32..30b94a6 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java @@ -6,17 +6,8 @@ import testbed2.testbed2_api.INestedStruct1Interface; import testbed2.testbed2_api.AbstractNestedStruct1Interface; import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; - +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java index 3911333..d510a25 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java @@ -6,17 +6,10 @@ import testbed2.testbed2_api.INestedStruct2Interface; import testbed2.testbed2_api.AbstractNestedStruct2Interface; import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; - +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java index e45ac66..ca082d2 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java @@ -6,17 +6,12 @@ import testbed2.testbed2_api.INestedStruct3Interface; import testbed2.testbed2_api.AbstractNestedStruct3Interface; import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_api.Struct4; import testbed2.testbed2_api.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_api.Enum3; - +import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/goldenmaster/testbed2/testbed2serviceexample/build.gradle b/goldenmaster/testbed2/testbed2serviceexample/build.gradle index d0466c2..a4027e4 100644 --- a/goldenmaster/testbed2/testbed2serviceexample/build.gradle +++ b/goldenmaster/testbed2/testbed2serviceexample/build.gradle @@ -2,6 +2,10 @@ plugins { alias(libs.plugins.android.application) } +group = "testbed2" +version = "1.0.0" + + android { namespace 'testbed2.testbed2serviceexample' compileSdk 35 diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java index 67a035f..0bedd49 100644 --- a/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java @@ -18,26 +18,6 @@ import testbed2.testbed2_android_service.ManyParamInterfaceServiceStarter; //import message type and parcelabe types -import testbed2.testbed2_api.Struct1; -import testbed2.testbed2_android_messenger.Struct1Parcelable; -import testbed2.testbed2_api.Struct2; -import testbed2.testbed2_android_messenger.Struct2Parcelable; -import testbed2.testbed2_api.Struct3; -import testbed2.testbed2_android_messenger.Struct3Parcelable; -import testbed2.testbed2_api.Struct4; -import testbed2.testbed2_android_messenger.Struct4Parcelable; -import testbed2.testbed2_api.NestedStruct1; -import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; -import testbed2.testbed2_api.NestedStruct2; -import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; -import testbed2.testbed2_api.NestedStruct3; -import testbed2.testbed2_android_messenger.NestedStruct3Parcelable; -import testbed2.testbed2_api.Enum1; -import testbed2.testbed2_android_messenger.Enum1Parcelable; -import testbed2.testbed2_api.Enum2; -import testbed2.testbed2_android_messenger.Enum2Parcelable; -import testbed2.testbed2_api.Enum3; -import testbed2.testbed2_android_messenger.Enum3Parcelable; import testbed2.testbed2_api.IManyParamInterfaceEventListener; import testbed2.testbed2_api.IManyParamInterface; From 92b389d9fdcc37c9b7371de068e53aa303024cba Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Mon, 27 Oct 2025 08:35:48 +0100 Subject: [PATCH 38/45] fix(android): set correct class loader on receive rpc resp --- .../counter_android_client/CounterClient.java | 16 ++++------------ .../EnumInterfaceClient.java | 12 ++++-------- .../tbNames_android_client/NamEsClient.java | 4 ++-- .../ParentIfClient.java | 14 ++++---------- .../SimpleLocalIfClient.java | 1 - .../SameEnum1InterfaceClient.java | 3 +-- .../SameEnum2InterfaceClient.java | 6 ++---- .../SameStruct1InterfaceClient.java | 3 +-- .../SameStruct2InterfaceClient.java | 6 ++---- .../SameEnum1InterfaceClient.java | 3 +-- .../SameEnum2InterfaceClient.java | 6 ++---- .../SameStruct1InterfaceClient.java | 3 +-- .../SameStruct2InterfaceClient.java | 6 ++---- .../NoPropertiesInterfaceClient.java | 3 +-- .../NoSignalsInterfaceClient.java | 3 +-- .../SimpleArrayInterfaceClient.java | 8 -------- .../SimpleInterfaceClient.java | 10 +--------- .../VoidInterfaceClient.java | 2 +- .../StructArray2InterfaceClient.java | 15 +++++---------- .../StructArrayInterfaceClient.java | 15 +++++---------- .../StructInterfaceClient.java | 12 ++++-------- .../ManyParamInterfaceClient.java | 4 ---- .../NestedStruct1InterfaceClient.java | 3 +-- .../NestedStruct2InterfaceClient.java | 6 ++---- .../NestedStruct3InterfaceClient.java | 9 +++------ templates/android/client/client.java.tpl | 4 +++- 26 files changed, 53 insertions(+), 124 deletions(-) diff --git a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java index fce0500..556a9d6 100644 --- a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java +++ b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java @@ -268,9 +268,7 @@ public void handleMessage(Message msg) case RPC_IncrementResp: { Bundle data = msg.getData(); - - // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. - data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -288,9 +286,7 @@ public void handleMessage(Message msg) case RPC_IncrementArrayResp: { Bundle data = msg.getData(); - - // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. - data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -308,9 +304,7 @@ public void handleMessage(Message msg) case RPC_DecrementResp: { Bundle data = msg.getData(); - - // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. - data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -328,9 +322,7 @@ public void handleMessage(Message msg) case RPC_DecrementArrayResp: { Bundle data = msg.getData(); - - // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. - data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java index 8fc6e2a..79449e9 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java @@ -298,8 +298,7 @@ public void handleMessage(Message msg) case RPC_Func0Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -317,8 +316,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -336,8 +334,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -355,8 +352,7 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java index 7769f20..2c61cff 100644 --- a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java +++ b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java @@ -267,7 +267,7 @@ public void handleMessage(Message msg) case RPC_SomeFunctionResp: { Bundle data = msg.getData(); - + data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -285,7 +285,7 @@ public void handleMessage(Message msg) case RPC_SomeFunction2Resp: { Bundle data = msg.getData(); - + data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java index fd6d5c9..045e5b3 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java @@ -295,8 +295,7 @@ public void handleMessage(Message msg) case RPC_LocalIfMethodResp: { Bundle data = msg.getData(); - - data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -314,8 +313,7 @@ public void handleMessage(Message msg) case RPC_LocalIfMethodListResp: { Bundle data = msg.getData(); - - data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -333,9 +331,7 @@ public void handleMessage(Message msg) case RPC_ImportedIfMethodResp: { Bundle data = msg.getData(); - - // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. - data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -353,9 +349,7 @@ public void handleMessage(Message msg) case RPC_ImportedIfMethodListResp: { Bundle data = msg.getData(); - - // all structs (even from other modules) are known at compile time (see gradle files) and share same PathClassLoader, any class loader provides access to it. - data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java index fc27bc0..55a9270 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java @@ -212,7 +212,6 @@ public void handleMessage(Message msg) case RPC_IntMethodResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java index 7c9895b..dbbb4e1 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java @@ -217,8 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java index 42e904f..2bf0e97 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java @@ -246,8 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -265,8 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java index 34943e3..1d43ac1 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java @@ -217,8 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java index 23c8cd5..09c6e94 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java @@ -246,8 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -265,8 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java index 7cfc11d..1721c34 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java @@ -217,8 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java index fb6ec38..d6c9de1 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java @@ -246,8 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -265,8 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java index e878cde..49c4199 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java @@ -217,8 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java index d9b2a01..772f055 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java @@ -246,8 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -265,8 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java index 211f01e..9adf237 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java @@ -205,7 +205,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); - + data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -223,7 +223,6 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java index e18546a..c8139e5 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java @@ -217,7 +217,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); - + data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -235,7 +235,6 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java index fb1a6e0..b231478 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java @@ -387,7 +387,6 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -405,7 +404,6 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -423,7 +421,6 @@ public void handleMessage(Message msg) case RPC_FuncInt32Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -441,7 +438,6 @@ public void handleMessage(Message msg) case RPC_FuncInt64Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -459,7 +455,6 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -477,7 +472,6 @@ public void handleMessage(Message msg) case RPC_FuncFloat32Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -495,7 +489,6 @@ public void handleMessage(Message msg) case RPC_FuncFloat64Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -513,7 +506,6 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java index ad2dcec..0d9c8a4 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java @@ -373,7 +373,7 @@ public void handleMessage(Message msg) case RPC_FuncNoReturnValueResp: { Bundle data = msg.getData(); - + data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -391,7 +391,6 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -409,7 +408,6 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -427,7 +425,6 @@ public void handleMessage(Message msg) case RPC_FuncInt32Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -445,7 +442,6 @@ public void handleMessage(Message msg) case RPC_FuncInt64Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -463,7 +459,6 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -481,7 +476,6 @@ public void handleMessage(Message msg) case RPC_FuncFloat32Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -499,7 +493,6 @@ public void handleMessage(Message msg) case RPC_FuncFloat64Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -517,7 +510,6 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java index 7a5a4a5..9836659 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java @@ -196,7 +196,7 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); - + data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java index e2298f9..2264b9e 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java @@ -325,8 +325,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructBoolWithArrayParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -344,8 +343,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructIntWithArrayParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -363,8 +361,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructFloatWithArrayParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -382,8 +379,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructStringWithArrayParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -401,8 +397,7 @@ public void handleMessage(Message msg) case RPC_FuncEnumResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructEnumWithArrayParcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java index 29a7253..f5a59fe 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java @@ -325,8 +325,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -344,8 +343,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -363,8 +361,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -382,8 +379,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -401,8 +397,7 @@ public void handleMessage(Message msg) case RPC_FuncEnumResp: { Bundle data = msg.getData(); - - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java index c3f8ded..08165c1 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java @@ -298,8 +298,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -317,8 +316,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -336,8 +334,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -355,8 +352,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java index 11423a4..2ce8d0e 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java @@ -293,7 +293,6 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -311,7 +310,6 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -329,7 +327,6 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -347,7 +344,6 @@ public void handleMessage(Message msg) case RPC_Func4Resp: { Bundle data = msg.getData(); - int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java index 73d0483..15707d7 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java @@ -217,8 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java index aa952ee..5ec8bbb 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java @@ -246,8 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -265,8 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java index 855ada3..6f1a0a4 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java @@ -277,8 +277,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -296,8 +295,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -315,8 +313,7 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index db09ff9..2c7b72f 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -359,7 +359,9 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface case RPC_{{Camel .Name}}Resp: { Bundle data = msg.getData(); - {{template "setClassLoaderIfNeeded" .Params}} + {{- if not (or .Return.IsPrimitive .Return.IsVoid)}} + data.setClassLoader({{template "getParcelable" .Return }}.class.getClassLoader()); + {{- end}} int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); From c94316592a8859bdae3d0f588cfc25b1c532b86b Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:01:12 +0100 Subject: [PATCH 39/45] fix: fix interfaces comparison --- templates/android/client/client.java.tpl | 10 ++++++---- templates/stub/implservice.java.tpl | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 2c7b72f..7d3e7d9 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -393,10 +393,11 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface Log.i(TAG, "request set{{Camel .Name}} called "+ {{javaVar . }}); {{- if .IsArray }} if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) - {{- else if or .IsPrimitive (eq .KindType "enum") }} + {{- else if or (or .IsPrimitive (eq .KindType "enum")) (eq .KindType "interface") }} if (m_{{javaVar .}} != {{javaVar .}}) {{- else }} - if (! m_{{javaVar .}}.equals({{javaVar .}})) + if ( (m_{{javaVar .}} != null && ! m_{{javaVar .}}.equals({{javaVar .}})) + || (m_{{javaVar .}} == null && {{javaVar .}} != null )) {{- end }} { Message msg = new Message(); @@ -414,10 +415,11 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface Log.i(TAG, "value received from service for {{Camel .Name}} "); {{- if .IsArray }} if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) - {{- else if or .IsPrimitive (eq .KindType "enum") }} + {{- else if or (or .IsPrimitive (eq .KindType "enum")) (eq .KindType "interface") }} if (m_{{javaVar .}} != {{javaVar .}}) {{- else }} - if (! m_{{javaVar .}}.equals({{javaVar .}})) + if ( (m_{{javaVar .}} != null && ! m_{{javaVar .}}.equals({{javaVar .}})) + || (m_{{javaVar .}} == null && {{javaVar .}} != null )) {{- end }} { m_{{javaVar .}} = {{javaVar .}}; diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index fd11683..1b5d6aa 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -95,10 +95,11 @@ public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface Log.i(TAG, "request set{{Camel .Name}} called "); {{- if .IsArray }} if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) - {{- else if or .IsPrimitive (eq .KindType "enum") }} + {{- else if or (or .IsPrimitive (eq .KindType "enum")) (eq .KindType "interface") }} if (m_{{javaVar .}} != {{javaVar .}}) {{- else }} - if (! m_{{javaVar .}}.equals({{javaVar .}})) + if ( (m_{{javaVar .}} != null && ! m_{{javaVar .}}.equals({{javaVar .}})) + || (m_{{javaVar .}} == null && {{javaVar .}} != null )) {{- end}} { m_{{javaVar .}} = {{javaVar .}}; From bdff2ffb5b80602e9777e317fce22fcf71bf0eaa Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:33:07 +0100 Subject: [PATCH 40/45] chores: update test-apis --- test-apis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-apis b/test-apis index 4c1e51e..f41c5f3 160000 --- a/test-apis +++ b/test-apis @@ -1 +1 @@ -Subproject commit 4c1e51ec75744be4271d5d03b8cfec57ec0836be +Subproject commit f41c5f39cbb072516d222246ea3c3338f92554e9 From 28b4582e1d2f96d2bbfb3c24928a120e559fe22e Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:17:14 +0100 Subject: [PATCH 41/45] fix: fix for ref interfaces tests --- .gitignore | 8 +- .../counter_android_client/build.gradle | 1 + .../counter_android_client/CounterClient.java | 20 +-- .../CounterClientTest.java | 3 + .../CounterServiceAdapterTest.java | 3 + .../counter/counter_impl/CounterService.java | 6 +- .../tbEnum/tbEnum_android_client/build.gradle | 1 + .../EnumInterfaceClient.java | 8 +- .../EnumInterfaceClientTest.java | 3 + .../EnumInterfaceServiceAdapterTest.java | 3 + .../tbIfaceimport_android_client/build.gradle | 1 + .../EmptyIfClientTest.java | 3 + .../EmptyIfServiceAdapterTest.java | 3 + .../tbNames_android_client/build.gradle | 1 + .../tbNames_android_client/NamEsClient.java | 2 - .../NamEsClientTest.java | 3 + .../NamEsServiceAdapterTest.java | 3 + .../tbRefIfaces_android_client/build.gradle | 2 + .../ParentIfClient.java | 16 +-- .../ParentIfClientTest.java | 50 +++---- .../SimpleLocalIfClientTest.java | 6 + .../ParentIfServiceAdapterTest.java | 42 +++--- .../SimpleLocalIfServiceAdapterTest.java | 6 + .../tbRefIfaces_impl/ParentIfService.java | 4 +- .../tbSame1_android_client/build.gradle | 1 + .../SameEnum1InterfaceClient.java | 2 +- .../SameEnum2InterfaceClient.java | 4 +- .../SameStruct1InterfaceClient.java | 8 +- .../SameStruct2InterfaceClient.java | 16 ++- .../SameEnum1InterfaceClientTest.java | 12 ++ .../SameEnum2InterfaceClientTest.java | 12 ++ .../SameStruct1InterfaceClientTest.java | 12 ++ .../SameStruct2InterfaceClientTest.java | 12 ++ .../SameEnum1InterfaceServiceAdapterTest.java | 12 ++ .../SameEnum2InterfaceServiceAdapterTest.java | 12 ++ ...ameStruct1InterfaceServiceAdapterTest.java | 12 ++ ...ameStruct2InterfaceServiceAdapterTest.java | 12 ++ .../SameStruct1InterfaceService.java | 3 +- .../SameStruct2InterfaceService.java | 6 +- .../tbSame2_android_client/build.gradle | 1 + .../SameEnum1InterfaceClient.java | 2 +- .../SameEnum2InterfaceClient.java | 4 +- .../SameStruct1InterfaceClient.java | 8 +- .../SameStruct2InterfaceClient.java | 16 ++- .../SameEnum1InterfaceClientTest.java | 12 ++ .../SameEnum2InterfaceClientTest.java | 12 ++ .../SameStruct1InterfaceClientTest.java | 12 ++ .../SameStruct2InterfaceClientTest.java | 12 ++ .../SameEnum1InterfaceServiceAdapterTest.java | 12 ++ .../SameEnum2InterfaceServiceAdapterTest.java | 12 ++ ...ameStruct1InterfaceServiceAdapterTest.java | 12 ++ ...ameStruct2InterfaceServiceAdapterTest.java | 12 ++ .../SameStruct1InterfaceService.java | 3 +- .../SameStruct2InterfaceService.java | 6 +- .../tbSimple_android_client/build.gradle | 1 + .../NoPropertiesInterfaceClient.java | 1 - .../NoSignalsInterfaceClient.java | 1 - .../SimpleInterfaceClient.java | 58 +++++++- .../VoidInterfaceClient.java | 1 - .../EmptyInterfaceClientTest.java | 21 +++ .../NoOperationsInterfaceClientTest.java | 21 +++ .../NoPropertiesInterfaceClientTest.java | 21 +++ .../NoSignalsInterfaceClientTest.java | 21 +++ .../SimpleArrayInterfaceClientTest.java | 21 +++ .../SimpleInterfaceClientTest.java | 62 +++++++++ .../VoidInterfaceClientTest.java | 21 +++ .../SimpleInterfaceMessageType.java | 34 ++--- .../SimpleInterfaceServiceAdapter.java | 27 ++++ .../EmptyInterfaceServiceAdapterTest.java | 21 +++ ...OperationsInterfaceServiceAdapterTest.java | 21 +++ ...PropertiesInterfaceServiceAdapterTest.java | 21 +++ .../NoSignalsInterfaceServiceAdapterTest.java | 21 +++ ...impleArrayInterfaceServiceAdapterTest.java | 21 +++ .../SimpleInterfaceServiceAdapterTest.java | 52 ++++++++ .../VoidInterfaceServiceAdapterTest.java | 21 +++ .../tbSimple_api/ISimpleInterface.java | 2 + .../tbSimple_impl/SimpleInterfaceService.java | 13 ++ .../SimpleInterfaceJniClient.java | 19 +++ .../SimpleInterfaceJniService.java | 14 ++ .../testbed1_android_client/build.gradle | 1 + .../StructArray2InterfaceClient.java | 40 +++--- .../StructArrayInterfaceClient.java | 10 +- .../StructInterfaceClient.java | 32 +++-- .../StructArray2InterfaceClientTest.java | 9 ++ .../StructArrayInterfaceClientTest.java | 9 ++ .../StructInterfaceClientTest.java | 9 ++ ...ructArray2InterfaceServiceAdapterTest.java | 9 ++ ...tructArrayInterfaceServiceAdapterTest.java | 9 ++ .../StructInterfaceServiceAdapterTest.java | 9 ++ .../StructArray2InterfaceService.java | 15 ++- .../testbed1_impl/StructInterfaceService.java | 12 +- .../testbed2_android_client/build.gradle | 1 + .../NestedStruct1InterfaceClient.java | 124 +++++++++++++++++- .../NestedStruct2InterfaceClient.java | 16 ++- .../NestedStruct3InterfaceClient.java | 24 ++-- .../ManyParamInterfaceClientTest.java | 12 ++ .../NestedStruct1InterfaceClientTest.java | 96 ++++++++++++++ .../NestedStruct2InterfaceClientTest.java | 12 ++ .../NestedStruct3InterfaceClientTest.java | 12 ++ .../NestedStruct1InterfaceMessageType.java | 8 +- .../NestedStruct1InterfaceServiceAdapter.java | 55 ++++++++ .../ManyParamInterfaceServiceAdapterTest.java | 12 ++ ...tedStruct1InterfaceServiceAdapterTest.java | 70 ++++++++++ ...tedStruct2InterfaceServiceAdapterTest.java | 12 ++ ...tedStruct3InterfaceServiceAdapterTest.java | 12 ++ .../testbed2_api/INestedStruct1Interface.java | 4 + .../NestedStruct1InterfaceService.java | 29 +++- .../NestedStruct2InterfaceService.java | 6 +- .../NestedStruct3InterfaceService.java | 9 +- .../NestedStruct1InterfaceJniClient.java | 38 ++++++ .../NestedStruct1InterfaceJniService.java | 28 ++++ templates/android/client/build.gradle.tpl | 9 ++ templates/android/client/clienttest.java.tpl | 11 +- .../service/serviceadaptertest.java.tpl | 7 +- 114 files changed, 1588 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 9e73984..27c8854 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ .DS_Store .vscode/ +.vs/ .idea/ -./test/ -/demo/app/bin -/demo/gradle +**/bin/ +**/.gradle/ +**/build/ +test/ diff --git a/goldenmaster/counter/counter_android_client/build.gradle b/goldenmaster/counter/counter_android_client/build.gradle index 857ee26..6160bf5 100644 --- a/goldenmaster/counter/counter_android_client/build.gradle +++ b/goldenmaster/counter/counter_android_client/build.gradle @@ -34,4 +34,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':counter_impl') } diff --git a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java index 556a9d6..106ec89 100644 --- a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java +++ b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java @@ -268,7 +268,7 @@ public void handleMessage(Message msg) case RPC_IncrementResp: { Bundle data = msg.getData(); - data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -286,7 +286,7 @@ public void handleMessage(Message msg) case RPC_IncrementArrayResp: { Bundle data = msg.getData(); - data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); + data.setClassLoader(externTypes.externTypes_android_messenger.MyVector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -304,7 +304,7 @@ public void handleMessage(Message msg) case RPC_DecrementResp: { Bundle data = msg.getData(); - data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -322,7 +322,7 @@ public void handleMessage(Message msg) case RPC_DecrementArrayResp: { Bundle data = msg.getData(); - data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); + data.setClassLoader(customTypes.customTypes_android_messenger.Vector3DParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -349,7 +349,8 @@ public void handleMessage(Message msg) public void setVector(customTypes.customTypes_api.Vector3D vector) { Log.i(TAG, "request setVector called "+ vector); - if (! m_vector.equals(vector)) + if ( (m_vector != null && ! m_vector.equals(vector)) + || (m_vector == null && vector != null )) { Message msg = new Message(); msg.what = CounterMessageType.PROP_Vector.getValue(); @@ -365,7 +366,8 @@ public void setVector(customTypes.customTypes_api.Vector3D vector) public void onVector(customTypes.customTypes_api.Vector3D vector) { Log.i(TAG, "value received from service for Vector "); - if (! m_vector.equals(vector)) + if ( (m_vector != null && ! m_vector.equals(vector)) + || (m_vector == null && vector != null )) { m_vector = vector; fireVectorChanged(vector); @@ -385,7 +387,8 @@ public customTypes.customTypes_api.Vector3D getVector() public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) { Log.i(TAG, "request setExternVector called "+ extern_vector); - if (! m_extern_vector.equals(extern_vector)) + if ( (m_extern_vector != null && ! m_extern_vector.equals(extern_vector)) + || (m_extern_vector == null && extern_vector != null )) { Message msg = new Message(); msg.what = CounterMessageType.PROP_ExternVector.getValue(); @@ -401,7 +404,8 @@ public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.V public void onExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) { Log.i(TAG, "value received from service for ExternVector "); - if (! m_extern_vector.equals(extern_vector)) + if ( (m_extern_vector != null && ! m_extern_vector.equals(extern_vector)) + || (m_extern_vector == null && extern_vector != null )) { m_extern_vector = extern_vector; fireExternVectorChanged(extern_vector); diff --git a/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java b/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java index 6692160..096f8fa 100644 --- a/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java +++ b/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java @@ -5,6 +5,9 @@ //import message type and parcelabe types import counter.counter_api.CounterTestHelper; +import counter.counter_api.ICounter; +import counter.counter_android_messenger.CounterParcelable; +import counter.counter_impl.CounterService; import counter.counter_api.ICounterEventListener; import counter.counter_api.ICounter; diff --git a/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java b/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java index 6c1b2bd..86a9685 100644 --- a/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java +++ b/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java @@ -18,6 +18,9 @@ //import message type and parcelabe types import counter.counter_api.CounterTestHelper; +import counter.counter_api.ICounter; +import counter.counter_android_messenger.CounterParcelable; +import counter.counter_impl.CounterService; import counter.counter_api.ICounterEventListener; diff --git a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java index 80df5e2..30bab8c 100644 --- a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java +++ b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java @@ -37,7 +37,8 @@ public CounterService() public void setVector(customTypes.customTypes_api.Vector3D vector) { Log.i(TAG, "request setVector called "); - if (! m_vector.equals(vector)) + if ( (m_vector != null && ! m_vector.equals(vector)) + || (m_vector == null && vector != null )) { m_vector = vector; onVectorChanged(m_vector); @@ -57,7 +58,8 @@ public customTypes.customTypes_api.Vector3D getVector() public void setExternVector(org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector) { Log.i(TAG, "request setExternVector called "); - if (! m_extern_vector.equals(extern_vector)) + if ( (m_extern_vector != null && ! m_extern_vector.equals(extern_vector)) + || (m_extern_vector == null && extern_vector != null )) { m_extern_vector = extern_vector; onExternVectorChanged(m_extern_vector); diff --git a/goldenmaster/tbEnum/tbEnum_android_client/build.gradle b/goldenmaster/tbEnum/tbEnum_android_client/build.gradle index 6af1f72..8095c67 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/build.gradle +++ b/goldenmaster/tbEnum/tbEnum_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbEnum_impl') } diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java index 79449e9..ab80de7 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java @@ -298,7 +298,7 @@ public void handleMessage(Message msg) case RPC_Func0Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -316,7 +316,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -334,7 +334,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum2Parcelable.class.getClassLoader()); + data.setClassLoader(Enum2Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -352,7 +352,7 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum3Parcelable.class.getClassLoader()); + data.setClassLoader(Enum3Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java index 1590b0f..809f3d7 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java @@ -13,6 +13,9 @@ import tbEnum.tbEnum_android_messenger.Enum2Parcelable; import tbEnum.tbEnum_api.Enum3; import tbEnum.tbEnum_android_messenger.Enum3Parcelable; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceParcelable; +import tbEnum.tbEnum_impl.EnumInterfaceService; import tbEnum.tbEnum_api.IEnumInterfaceEventListener; import tbEnum.tbEnum_api.IEnumInterface; diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java index b71b215..4eb8434 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java @@ -26,6 +26,9 @@ import tbEnum.tbEnum_android_messenger.Enum2Parcelable; import tbEnum.tbEnum_api.Enum3; import tbEnum.tbEnum_android_messenger.Enum3Parcelable; +import tbEnum.tbEnum_api.IEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceParcelable; +import tbEnum.tbEnum_impl.EnumInterfaceService; import tbEnum.tbEnum_api.IEnumInterfaceEventListener; diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle index 7f9b59b..4341235 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbIfaceimport_impl') } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java index c253903..57cc7ed 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java @@ -5,6 +5,9 @@ //import message type and parcelabe types import tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable; +import tbIfaceimport.tbIfaceimport_impl.EmptyIfService; import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; import tbIfaceimport.tbIfaceimport_api.IEmptyIf; diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java index 24d9b7a..ec1d279 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java @@ -18,6 +18,9 @@ //import message type and parcelabe types import tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper; +import tbIfaceimport.tbIfaceimport_api.IEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable; +import tbIfaceimport.tbIfaceimport_impl.EmptyIfService; import tbIfaceimport.tbIfaceimport_api.IEmptyIfEventListener; diff --git a/goldenmaster/tbNames/tbNames_android_client/build.gradle b/goldenmaster/tbNames/tbNames_android_client/build.gradle index 49e70e2..2db2282 100644 --- a/goldenmaster/tbNames/tbNames_android_client/build.gradle +++ b/goldenmaster/tbNames/tbNames_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbNames_impl') } diff --git a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java index 2c61cff..9e38fc1 100644 --- a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java +++ b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java @@ -267,7 +267,6 @@ public void handleMessage(Message msg) case RPC_SomeFunctionResp: { Bundle data = msg.getData(); - data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -285,7 +284,6 @@ public void handleMessage(Message msg) case RPC_SomeFunction2Resp: { Bundle data = msg.getData(); - data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java index 19de423..c97b6db 100644 --- a/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java +++ b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java @@ -7,6 +7,9 @@ import tbNames.tbNames_api.TbNamesTestHelper; import tbNames.tbNames_api.EnumWithUnderScores; import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_android_messenger.NamEsParcelable; +import tbNames.tbNames_impl.NamEsService; import tbNames.tbNames_api.INamEsEventListener; import tbNames.tbNames_api.INamEs; diff --git a/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java index f4a16a1..86458e5 100644 --- a/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java +++ b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java @@ -20,6 +20,9 @@ import tbNames.tbNames_api.TbNamesTestHelper; import tbNames.tbNames_api.EnumWithUnderScores; import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_android_messenger.NamEsParcelable; +import tbNames.tbNames_impl.NamEsService; import tbNames.tbNames_api.INamEsEventListener; diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle index 582feff..9888cd1 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle @@ -33,4 +33,6 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbRefIfaces_impl') + testImplementation 'tbIfaceimport:tbIfaceimport_impl:1.0.0' } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java index 045e5b3..4ef36cc 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java @@ -295,7 +295,7 @@ public void handleMessage(Message msg) case RPC_LocalIfMethodResp: { Bundle data = msg.getData(); - data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -313,7 +313,7 @@ public void handleMessage(Message msg) case RPC_LocalIfMethodListResp: { Bundle data = msg.getData(); - data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); + data.setClassLoader(SimpleLocalIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -331,7 +331,7 @@ public void handleMessage(Message msg) case RPC_ImportedIfMethodResp: { Bundle data = msg.getData(); - data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -349,7 +349,7 @@ public void handleMessage(Message msg) case RPC_ImportedIfMethodListResp: { Bundle data = msg.getData(); - data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); + data.setClassLoader(tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -376,7 +376,7 @@ public void handleMessage(Message msg) public void setLocalIf(ISimpleLocalIf localIf) { Log.i(TAG, "request setLocalIf called "+ localIf); - if (! m_localIf.equals(localIf)) + if (m_localIf != localIf) { Message msg = new Message(); msg.what = ParentIfMessageType.PROP_LocalIf.getValue(); @@ -392,7 +392,7 @@ public void setLocalIf(ISimpleLocalIf localIf) public void onLocalIf(ISimpleLocalIf localIf) { Log.i(TAG, "value received from service for LocalIf "); - if (! m_localIf.equals(localIf)) + if (m_localIf != localIf) { m_localIf = localIf; fireLocalIfChanged(localIf); @@ -448,7 +448,7 @@ public ISimpleLocalIf[] getLocalIfList() public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) { Log.i(TAG, "request setImportedIf called "+ importedIf); - if (! m_importedIf.equals(importedIf)) + if (m_importedIf != importedIf) { Message msg = new Message(); msg.what = ParentIfMessageType.PROP_ImportedIf.getValue(); @@ -464,7 +464,7 @@ public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) public void onImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) { Log.i(TAG, "value received from service for ImportedIf "); - if (! m_importedIf.equals(importedIf)) + if (m_importedIf != importedIf) { m_importedIf = importedIf; fireImportedIfChanged(importedIf); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java index f0edf43..238a6a8 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java @@ -5,6 +5,12 @@ //import message type and parcelabe types import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.ParentIfService; import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; import tbRefIfaces.tbRefIfaces_api.IParentIf; @@ -136,15 +142,15 @@ public void onInitReceive() throws RemoteException Message msg = Message.obtain(null, ParentIfMessageType.INIT.getValue()); Bundle data = new Bundle(); - ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; - testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); - tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testimportedIf)); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testimportedIfList)); //setup mock expectations @@ -169,7 +175,7 @@ public void onReceivelocalIfPropertyChangeTest() throws RemoteException { // Create and send message Message msg = Message.obtain(null, ParentIfMessageType.SET_LocalIf.getValue()); Bundle data = new Bundle(); - ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); msg.setData(data); @@ -184,7 +190,7 @@ public void onReceivelocalIfPropertyChangeTest() throws RemoteException { @Test public void setPropertyRequestlocalIf() { - ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); testedClient.setLocalIf(testlocalIf); Robolectric.flushForegroundThreadScheduler(); @@ -206,7 +212,7 @@ public void onReceivelocalIfListPropertyChangeTest() throws RemoteException { Message msg = Message.obtain(null, ParentIfMessageType.SET_LocalIfList.getValue()); Bundle data = new Bundle(); ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; - testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); msg.setData(data); @@ -222,7 +228,7 @@ public void onReceivelocalIfListPropertyChangeTest() throws RemoteException { public void setPropertyRequestlocalIfList() { ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; - testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); testedClient.setLocalIfList(testlocalIfList); Robolectric.flushForegroundThreadScheduler(); @@ -243,7 +249,7 @@ public void onReceiveimportedIfPropertyChangeTest() throws RemoteException { // Create and send message Message msg = Message.obtain(null, ParentIfMessageType.SET_ImportedIf.getValue()); Bundle data = new Bundle(); - tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testimportedIf)); msg.setData(data); @@ -258,7 +264,7 @@ public void onReceiveimportedIfPropertyChangeTest() throws RemoteException { @Test public void setPropertyRequestimportedIf() { - tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); testedClient.setImportedIf(testimportedIf); Robolectric.flushForegroundThreadScheduler(); @@ -280,7 +286,7 @@ public void onReceiveimportedIfListPropertyChangeTest() throws RemoteException { Message msg = Message.obtain(null, ParentIfMessageType.SET_ImportedIfList.getValue()); Bundle data = new Bundle(); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testimportedIfList)); msg.setData(data); @@ -296,7 +302,7 @@ public void onReceiveimportedIfListPropertyChangeTest() throws RemoteException { public void setPropertyRequestimportedIfList() { tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); testedClient.setImportedIfList(testimportedIfList); Robolectric.flushForegroundThreadScheduler(); @@ -318,7 +324,7 @@ public void whenNotifiedlocalIfSignal() throws RemoteException Message msg = Message.obtain(null, ParentIfMessageType.SIG_LocalIfSignal.getValue()); Bundle data = new Bundle(); - ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelable("param", new SimpleLocalIfParcelable(testparam)); msg.setData(data); @@ -335,7 +341,7 @@ public void whenNotifiedlocalIfSignalList() throws RemoteException Message msg = Message.obtain(null, ParentIfMessageType.SIG_LocalIfSignalList.getValue()); Bundle data = new Bundle(); ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; - testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(testparam)); msg.setData(data); @@ -351,7 +357,7 @@ public void whenNotifiedimportedIfSignal() throws RemoteException Message msg = Message.obtain(null, ParentIfMessageType.SIG_ImportedIfSignal.getValue()); Bundle data = new Bundle(); - tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelable("param", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testparam)); msg.setData(data); @@ -368,7 +374,7 @@ public void whenNotifiedimportedIfSignalList() throws RemoteException Message msg = Message.obtain(null, ParentIfMessageType.SIG_ImportedIfSignalList.getValue()); Bundle data = new Bundle(); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testparam)); msg.setData(data); @@ -383,7 +389,7 @@ public void whenNotifiedimportedIfSignalList() throws RemoteException public void onlocalIfMethodRequest() throws RemoteException { // Execute method - ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); ISimpleLocalIf expectedResult = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); AtomicBoolean receivedResp = new AtomicBoolean(false); @@ -430,9 +436,9 @@ public void onlocalIfMethodListRequest() throws RemoteException { // Execute method ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; - testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); ISimpleLocalIf[] expectedResult = new ISimpleLocalIf[1]; - expectedResult[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + expectedResult[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); AtomicBoolean receivedResp = new AtomicBoolean(false); CompletableFuture resFuture = testedClient.localIfMethodListAsync(testparam); @@ -477,7 +483,7 @@ public void onlocalIfMethodListRequest() throws RemoteException { public void onimportedIfMethodRequest() throws RemoteException { // Execute method - tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); tbIfaceimport.tbIfaceimport_api.IEmptyIf expectedResult = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); AtomicBoolean receivedResp = new AtomicBoolean(false); @@ -525,9 +531,9 @@ public void onimportedIfMethodListRequest() throws RemoteException { // Execute method tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] expectedResult = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - expectedResult[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + expectedResult[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); AtomicBoolean receivedResp = new AtomicBoolean(false); CompletableFuture resFuture = testedClient.importedIfMethodListAsync(testparam); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java index 50f50d2..86dbee8 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java @@ -5,6 +5,12 @@ //import message type and parcelabe types import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.ParentIfService; import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java index d0eee18..f65247b 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java @@ -18,6 +18,12 @@ //import message type and parcelabe types import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.ParentIfService; import tbRefIfaces.tbRefIfaces_api.IParentIfEventListener; @@ -227,7 +233,7 @@ public void onReceivelocalIfPropertyChangeTest() throws RemoteException { // Create and send message Message msg = Message.obtain(null, ParentIfMessageType.PROP_LocalIf.getValue()); Bundle data = new Bundle(); - ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); msg.setData(data); @@ -240,7 +246,7 @@ public void onReceivelocalIfPropertyChangeTest() throws RemoteException { @Test public void whenNotifiedlocalIf() { - ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testlocalIf = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); testedAdapterAsEventListener.onLocalIfChanged(testlocalIf); Robolectric.flushForegroundThreadScheduler(); @@ -262,7 +268,7 @@ public void onReceivelocalIfListPropertyChangeTest() throws RemoteException { Message msg = Message.obtain(null, ParentIfMessageType.PROP_LocalIfList.getValue()); Bundle data = new Bundle(); ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; - testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); msg.setData(data); @@ -276,7 +282,7 @@ public void onReceivelocalIfListPropertyChangeTest() throws RemoteException { public void whenNotifiedlocalIfList() { ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; - testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); testedAdapterAsEventListener.onLocalIfListChanged(testlocalIfList); Robolectric.flushForegroundThreadScheduler(); @@ -297,7 +303,7 @@ public void onReceiveimportedIfPropertyChangeTest() throws RemoteException { // Create and send message Message msg = Message.obtain(null, ParentIfMessageType.PROP_ImportedIf.getValue()); Bundle data = new Bundle(); - tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelable("importedIf", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testimportedIf)); msg.setData(data); @@ -310,7 +316,7 @@ public void onReceiveimportedIfPropertyChangeTest() throws RemoteException { @Test public void whenNotifiedimportedIf() { - tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testimportedIf = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); testedAdapterAsEventListener.onImportedIfChanged(testimportedIf); Robolectric.flushForegroundThreadScheduler(); @@ -332,7 +338,7 @@ public void onReceiveimportedIfListPropertyChangeTest() throws RemoteException { Message msg = Message.obtain(null, ParentIfMessageType.PROP_ImportedIfList.getValue()); Bundle data = new Bundle(); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelableArray("importedIfList", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testimportedIfList)); msg.setData(data); @@ -346,7 +352,7 @@ public void onReceiveimportedIfListPropertyChangeTest() throws RemoteException { public void whenNotifiedimportedIfList() { tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testimportedIfList = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testimportedIfList[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); testedAdapterAsEventListener.onImportedIfListChanged(testimportedIfList); Robolectric.flushForegroundThreadScheduler(); @@ -365,7 +371,7 @@ public void whenNotifiedimportedIfList() @Test public void whenNotifiedlocalIfSignal() { - ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); testedAdapterAsEventListener.onLocalIfSignal(testparam); Robolectric.flushForegroundThreadScheduler(); @@ -385,7 +391,7 @@ public void whenNotifiedlocalIfSignal() public void whenNotifiedlocalIfSignalList() { ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; - testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); testedAdapterAsEventListener.onLocalIfSignalList(testparam); Robolectric.flushForegroundThreadScheduler(); @@ -404,7 +410,7 @@ public void whenNotifiedlocalIfSignalList() @Test public void whenNotifiedimportedIfSignal() { - tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); testedAdapterAsEventListener.onImportedIfSignal(testparam); Robolectric.flushForegroundThreadScheduler(); @@ -425,7 +431,7 @@ public void whenNotifiedimportedIfSignal() public void whenNotifiedimportedIfSignalList() { tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); testedAdapterAsEventListener.onImportedIfSignalList(testparam); Robolectric.flushForegroundThreadScheduler(); @@ -451,7 +457,7 @@ public void onlocalIfMethodRequest() throws RemoteException { int callId = 99; data.putInt("callId", callId); - ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); + ISimpleLocalIf testparam = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelable("param", new SimpleLocalIfParcelable(testparam)); ISimpleLocalIf returnedValue = TbRefIfacesTestHelper.makeTestSimpleLocalIf(null); @@ -486,10 +492,10 @@ public void onlocalIfMethodListRequest() throws RemoteException { int callId = 99; data.putInt("callId", callId); ISimpleLocalIf[] testparam = new ISimpleLocalIf[1]; - testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + testparam[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(testparam)); ISimpleLocalIf[] returnedValue = new ISimpleLocalIf[1]; - returnedValue[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new ISimpleLocalIf[]{}); + returnedValue[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); when(backendServiceMock.localIfMethodList( any(ISimpleLocalIf[].class))).thenReturn(returnedValue); @@ -521,7 +527,7 @@ public void onimportedIfMethodRequest() throws RemoteException { int callId = 99; data.putInt("callId", callId); - tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); + tbIfaceimport.tbIfaceimport_api.IEmptyIf testparam = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelable("param", new tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable(testparam)); tbIfaceimport.tbIfaceimport_api.IEmptyIf returnedValue = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(null); @@ -556,10 +562,10 @@ public void onimportedIfMethodListRequest() throws RemoteException { int callId = 99; data.putInt("callId", callId); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] testparam = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + testparam[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); data.putParcelableArray("param", tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable.wrapArray(testparam)); tbIfaceimport.tbIfaceimport_api.IEmptyIf[] returnedValue = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; - returnedValue[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}); + returnedValue[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); when(backendServiceMock.importedIfMethodList( any(tbIfaceimport.tbIfaceimport_api.IEmptyIf[].class))).thenReturn(returnedValue); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java index 3a2db2e..a56085c 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java @@ -18,6 +18,12 @@ //import message type and parcelabe types import tbRefIfaces.tbRefIfaces_api.TbRefIfacesTestHelper; +import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIf; +import tbRefIfaces.tbRefIfaces_android_messenger.SimpleLocalIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.SimpleLocalIfService; +import tbRefIfaces.tbRefIfaces_api.IParentIf; +import tbRefIfaces.tbRefIfaces_android_messenger.ParentIfParcelable; +import tbRefIfaces.tbRefIfaces_impl.ParentIfService; import tbRefIfaces.tbRefIfaces_api.ISimpleLocalIfEventListener; diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java index 5a9d922..b67b9df 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java @@ -39,7 +39,7 @@ public ParentIfService() public void setLocalIf(ISimpleLocalIf localIf) { Log.i(TAG, "request setLocalIf called "); - if (! m_localIf.equals(localIf)) + if (m_localIf != localIf) { m_localIf = localIf; onLocalIfChanged(m_localIf); @@ -79,7 +79,7 @@ public ISimpleLocalIf[] getLocalIfList() public void setImportedIf(tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIf) { Log.i(TAG, "request setImportedIf called "); - if (! m_importedIf.equals(importedIf)) + if (m_importedIf != importedIf) { m_importedIf = importedIf; onImportedIfChanged(m_importedIf); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/build.gradle b/goldenmaster/tbSame1/tbSame1_android_client/build.gradle index 81f8529..a400c4c 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/build.gradle +++ b/goldenmaster/tbSame1/tbSame1_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbSame1_impl') } diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java index dbbb4e1..b2b1f7f 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java @@ -217,7 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java index 2bf0e97..10ac29d 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java @@ -246,7 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -264,7 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java index 1d43ac1..18b0363 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java @@ -217,7 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -244,7 +244,8 @@ public void handleMessage(Message msg) public void setProp1(Struct1 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = SameStruct1InterfaceMessageType.PROP_Prop1.getValue(); @@ -260,7 +261,8 @@ public void setProp1(Struct1 prop1) public void onProp1(Struct1 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java index 09c6e94..ef67fc3 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java @@ -246,7 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -264,7 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -291,7 +291,8 @@ public void handleMessage(Message msg) public void setProp1(Struct2 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = SameStruct2InterfaceMessageType.PROP_Prop1.getValue(); @@ -307,7 +308,8 @@ public void setProp1(Struct2 prop1) public void onProp1(Struct2 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); @@ -327,7 +329,8 @@ public Struct2 getProp1() public void setProp2(Struct2 prop2) { Log.i(TAG, "request setProp2 called "+ prop2); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { Message msg = new Message(); msg.what = SameStruct2InterfaceMessageType.PROP_Prop2.getValue(); @@ -343,7 +346,8 @@ public void setProp2(Struct2 prop2) public void onProp2(Struct2 prop2) { Log.i(TAG, "value received from service for Prop2 "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; fireProp2Changed(prop2); diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java index d98d7cb..66f041c 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; import tbSame1.tbSame1_api.ISameEnum1Interface; diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java index aa868ee..dc6ef7e 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; import tbSame1.tbSame1_api.ISameEnum2Interface; diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java index f138a5d..d1c7366 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct1Interface; diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java index 78b7a43..296d01c 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; import tbSame1.tbSame1_api.ISameStruct2Interface; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java index 4f1a7f1..0dd0f4a 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java index 3100f3b..dca942c 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java index 37f6baa..7e56738 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java index 3ccb9f3..fe868e8 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame1.tbSame1_android_messenger.Enum1Parcelable; import tbSame1.tbSame1_api.Enum2; import tbSame1.tbSame1_android_messenger.Enum2Parcelable; +import tbSame1.tbSame1_api.ISameStruct1Interface; +import tbSame1.tbSame1_android_messenger.SameStruct1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct1InterfaceService; +import tbSame1.tbSame1_api.ISameStruct2Interface; +import tbSame1.tbSame1_android_messenger.SameStruct2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameStruct2InterfaceService; +import tbSame1.tbSame1_api.ISameEnum1Interface; +import tbSame1.tbSame1_android_messenger.SameEnum1InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum1InterfaceService; +import tbSame1.tbSame1_api.ISameEnum2Interface; +import tbSame1.tbSame1_android_messenger.SameEnum2InterfaceParcelable; +import tbSame1.tbSame1_impl.SameEnum2InterfaceService; import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java index 587e620..1c2426f 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java @@ -35,7 +35,8 @@ public SameStruct1InterfaceService() public void setProp1(Struct1 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java index bb6cae8..6aeb87d 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java @@ -37,7 +37,8 @@ public SameStruct2InterfaceService() public void setProp1(Struct2 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); @@ -57,7 +58,8 @@ public Struct2 getProp1() public void setProp2(Struct2 prop2) { Log.i(TAG, "request setProp2 called "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; onProp2Changed(m_prop2); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/build.gradle b/goldenmaster/tbSame2/tbSame2_android_client/build.gradle index dd2a82d..afbc515 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/build.gradle +++ b/goldenmaster/tbSame2/tbSame2_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbSame2_impl') } diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java index 1721c34..4cda64a 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java @@ -217,7 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java index d6c9de1..986040e 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java @@ -246,7 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -264,7 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Enum1Parcelable.class.getClassLoader()); + data.setClassLoader(Enum1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java index 49c4199..255970e 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java @@ -217,7 +217,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -244,7 +244,8 @@ public void handleMessage(Message msg) public void setProp1(Struct1 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = SameStruct1InterfaceMessageType.PROP_Prop1.getValue(); @@ -260,7 +261,8 @@ public void setProp1(Struct1 prop1) public void onProp1(Struct1 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java index 772f055..de2d102 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java @@ -246,7 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -264,7 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(Struct1Parcelable.class.getClassLoader()); + data.setClassLoader(Struct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -291,7 +291,8 @@ public void handleMessage(Message msg) public void setProp1(Struct2 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = SameStruct2InterfaceMessageType.PROP_Prop1.getValue(); @@ -307,7 +308,8 @@ public void setProp1(Struct2 prop1) public void onProp1(Struct2 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); @@ -327,7 +329,8 @@ public Struct2 getProp1() public void setProp2(Struct2 prop2) { Log.i(TAG, "request setProp2 called "+ prop2); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { Message msg = new Message(); msg.what = SameStruct2InterfaceMessageType.PROP_Prop2.getValue(); @@ -343,7 +346,8 @@ public void setProp2(Struct2 prop2) public void onProp2(Struct2 prop2) { Log.i(TAG, "value received from service for Prop2 "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; fireProp2Changed(prop2); diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java index 6bc91b1..dea7280 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; import tbSame2.tbSame2_api.ISameEnum1Interface; diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java index e14f559..fec620f 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; import tbSame2.tbSame2_api.ISameEnum2Interface; diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java index 3c96666..6709813 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct1Interface; diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java index a7adec9..6ff5131 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java @@ -13,6 +13,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; import tbSame2.tbSame2_api.ISameStruct2Interface; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java index 17560b0..501b0ab 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java index 9cd942e..19b3b44 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java index 6b96ccb..c927b32 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java index 296fe2a..7b1ab42 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -26,6 +26,18 @@ import tbSame2.tbSame2_android_messenger.Enum1Parcelable; import tbSame2.tbSame2_api.Enum2; import tbSame2.tbSame2_android_messenger.Enum2Parcelable; +import tbSame2.tbSame2_api.ISameStruct1Interface; +import tbSame2.tbSame2_android_messenger.SameStruct1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct1InterfaceService; +import tbSame2.tbSame2_api.ISameStruct2Interface; +import tbSame2.tbSame2_android_messenger.SameStruct2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameStruct2InterfaceService; +import tbSame2.tbSame2_api.ISameEnum1Interface; +import tbSame2.tbSame2_android_messenger.SameEnum1InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum1InterfaceService; +import tbSame2.tbSame2_api.ISameEnum2Interface; +import tbSame2.tbSame2_android_messenger.SameEnum2InterfaceParcelable; +import tbSame2.tbSame2_impl.SameEnum2InterfaceService; import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java index d17fde5..bf2d301 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java @@ -35,7 +35,8 @@ public SameStruct1InterfaceService() public void setProp1(Struct1 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java index 29bf92b..8260d2d 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java @@ -37,7 +37,8 @@ public SameStruct2InterfaceService() public void setProp1(Struct2 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); @@ -57,7 +58,8 @@ public Struct2 getProp1() public void setProp2(Struct2 prop2) { Log.i(TAG, "request setProp2 called "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; onProp2Changed(m_prop2); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/build.gradle b/goldenmaster/tbSimple/tbSimple_android_client/build.gradle index e1c7844..418a6b6 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/build.gradle +++ b/goldenmaster/tbSimple/tbSimple_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':tbSimple_impl') } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java index 9adf237..afa2b36 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java @@ -205,7 +205,6 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); - data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java index c8139e5..20e58ea 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java @@ -217,7 +217,6 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); - data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java index 0d9c8a4..8c65493 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java @@ -373,7 +373,6 @@ public void handleMessage(Message msg) case RPC_FuncNoReturnValueResp: { Bundle data = msg.getData(); - data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -387,6 +386,23 @@ public void handleMessage(Message msg) } break; + } + case RPC_FuncNoParamsResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received SimpleInterfaceMessageType.RPC_FuncNoParamsResp , could not find pending call for " + msg.obj); + } + break; + } case RPC_FuncBoolResp: { @@ -864,6 +880,46 @@ public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { } + @Override + public boolean funcNoParams() { + CompletableFuture resFuture = funcNoParamsAsync(); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcNoParamsAsync() { + + Log.i(TAG, "Call on service funcNoParams "); + Message msg = new Message(); + msg.what = SimpleInterfaceMessageType.RPC_FuncNoParamsReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + boolean result = bundle.getBoolean("result", false); + Log.v(TAG, "resolve funcNoParams" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override public boolean funcBool(boolean paramBool) { CompletableFuture resFuture = funcBoolAsync(paramBool); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java index 9836659..b172b68 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java @@ -196,7 +196,6 @@ public void handleMessage(Message msg) case RPC_FuncVoidResp: { Bundle data = msg.getData(); - data.setClassLoader(VoidParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java index 427bac5..6d4403b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; import tbSimple.tbSimple_api.IEmptyInterface; diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java index a9f201d..1abd4e2 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; import tbSimple.tbSimple_api.INoOperationsInterface; diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java index f7bf3a0..95be451 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; import tbSimple.tbSimple_api.INoPropertiesInterface; diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java index a96a0b9..71c089d 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; import tbSimple.tbSimple_api.INoSignalsInterface; diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java index 4f11a71..427e321 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; import tbSimple.tbSimple_api.ISimpleArrayInterface; diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java index 77f1ed6..dba17f3 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; import tbSimple.tbSimple_api.ISimpleInterface; @@ -586,6 +607,47 @@ public void onfuncNoReturnValueRequest() throws RemoteException { } + public void onfuncNoParamsRequest() throws RemoteException { + + // Execute method + boolean expectedResult = true; + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcNoParamsAsync(); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result.booleanValue()); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(SimpleInterfaceMessageType.RPC_FuncNoParamsReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncNoParamsResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putBoolean("result", expectedResult); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + public void onfuncBoolRequest() throws RemoteException { // Execute method diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java index 0971dc7..06c2e07 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java @@ -5,6 +5,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.IVoidInterfaceEventListener; import tbSimple.tbSimple_api.IVoidInterface; diff --git a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java index 917dcc3..f36499d 100644 --- a/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java @@ -30,22 +30,24 @@ public enum SimpleInterfaceMessageType { SIG_SigString(26), RPC_FuncNoReturnValueReq(27), RPC_FuncNoReturnValueResp(28), - RPC_FuncBoolReq(29), - RPC_FuncBoolResp(30), - RPC_FuncIntReq(31), - RPC_FuncIntResp(32), - RPC_FuncInt32Req(33), - RPC_FuncInt32Resp(34), - RPC_FuncInt64Req(35), - RPC_FuncInt64Resp(36), - RPC_FuncFloatReq(37), - RPC_FuncFloatResp(38), - RPC_FuncFloat32Req(39), - RPC_FuncFloat32Resp(40), - RPC_FuncFloat64Req(41), - RPC_FuncFloat64Resp(42), - RPC_FuncStringReq(43), - RPC_FuncStringResp(44), + RPC_FuncNoParamsReq(29), + RPC_FuncNoParamsResp(30), + RPC_FuncBoolReq(31), + RPC_FuncBoolResp(32), + RPC_FuncIntReq(33), + RPC_FuncIntResp(34), + RPC_FuncInt32Req(35), + RPC_FuncInt32Resp(36), + RPC_FuncInt64Req(37), + RPC_FuncInt64Resp(38), + RPC_FuncFloatReq(39), + RPC_FuncFloatResp(40), + RPC_FuncFloat32Req(41), + RPC_FuncFloat32Resp(42), + RPC_FuncFloat64Req(43), + RPC_FuncFloat64Resp(44), + RPC_FuncStringReq(45), + RPC_FuncStringResp(46), SimpleInterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); private final int value; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java index 9ce874b..1541394 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java @@ -295,6 +295,33 @@ public void handleMessage(Message msg) } // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncNoParamsReq: { + + Bundle data = msg.getData(); + + int callId = data.getInt("callId"); + + boolean result = mBackendService.funcNoParams(); + + Message respMsg = new Message(); + respMsg.what = SimpleInterfaceMessageType.RPC_FuncNoParamsResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putBoolean("result", result); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. case RPC_FuncBoolReq: { diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java index 009c9e9..516ef8c 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java index 4ed51c6..971067f 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java index 1813cfa..36bce5b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java index 419325f..8e323fe 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java index 266d40c..93e4141 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java index aa0ffc5..f5562ad 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; @@ -691,6 +712,37 @@ public void onfuncNoReturnValueRequest() throws RemoteException { } + public void onfuncNoParamsRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncNoParamsReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + boolean returnedValue = true; + + + when(backendServiceMock.funcNoParams()).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcNoParams(); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(SimpleInterfaceMessageType.RPC_FuncNoParamsResp.getValue(), response.what); + Bundle resp_data = response.getData(); + boolean receivedByClient = resp_data.getBoolean("result", false); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + public void onfuncBoolRequest() throws RemoteException { // Create and send message Message msg = Message.obtain(null, SimpleInterfaceMessageType.RPC_FuncBoolReq.getValue()); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java index 157259e..d6a701e 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java @@ -18,6 +18,27 @@ //import message type and parcelabe types import tbSimple.tbSimple_api.TbSimpleTestHelper; +import tbSimple.tbSimple_api.IVoidInterface; +import tbSimple.tbSimple_android_messenger.VoidInterfaceParcelable; +import tbSimple.tbSimple_impl.VoidInterfaceService; +import tbSimple.tbSimple_api.ISimpleInterface; +import tbSimple.tbSimple_android_messenger.SimpleInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleInterfaceService; +import tbSimple.tbSimple_api.ISimpleArrayInterface; +import tbSimple.tbSimple_android_messenger.SimpleArrayInterfaceParcelable; +import tbSimple.tbSimple_impl.SimpleArrayInterfaceService; +import tbSimple.tbSimple_api.INoPropertiesInterface; +import tbSimple.tbSimple_android_messenger.NoPropertiesInterfaceParcelable; +import tbSimple.tbSimple_impl.NoPropertiesInterfaceService; +import tbSimple.tbSimple_api.INoOperationsInterface; +import tbSimple.tbSimple_android_messenger.NoOperationsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoOperationsInterfaceService; +import tbSimple.tbSimple_api.INoSignalsInterface; +import tbSimple.tbSimple_android_messenger.NoSignalsInterfaceParcelable; +import tbSimple.tbSimple_impl.NoSignalsInterfaceService; +import tbSimple.tbSimple_api.IEmptyInterface; +import tbSimple.tbSimple_android_messenger.EmptyInterfaceParcelable; +import tbSimple.tbSimple_impl.EmptyInterfaceService; import tbSimple.tbSimple_api.IVoidInterfaceEventListener; diff --git a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java index b02897b..1b0a95a 100644 --- a/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java @@ -42,6 +42,8 @@ public interface ISimpleInterface { // methods void funcNoReturnValue(boolean paramBool); CompletableFuture funcNoReturnValueAsync(boolean paramBool); + boolean funcNoParams(); + CompletableFuture funcNoParamsAsync(); boolean funcBool(boolean paramBool); CompletableFuture funcBoolAsync(boolean paramBool); int funcInt(int paramInt); diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java index 01e7ad0..ccdeae2 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java @@ -212,6 +212,19 @@ public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { executor); } + @Override + public boolean funcNoParams() { + Log.w(TAG, "request method funcNoParams called, returnig default"); + return false; + } + + @Override + public CompletableFuture funcNoParamsAsync() { + return CompletableFuture.supplyAsync( + () -> {return funcNoParams(); }, + executor); + } + @Override public boolean funcBool(boolean paramBool) { Log.w(TAG, "request method funcBool called, returnig default"); diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java index cdab0e0..33c9054 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java @@ -150,6 +150,24 @@ public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { Log.v(TAG, "NON Blocking call method "); return mMessengerClient.funcNoReturnValueAsync(paramBool); + } + public boolean funcNoParams() + { + Log.v(TAG, "Blocking callfuncNoParams - should not be used "); + return mMessengerClient.funcNoParams(); + } + + public void funcNoParamsAsync(String callId){ + Log.v(TAG, "non blocking call funcNoParams "); + mMessengerClient.funcNoParamsAsync().thenAccept(i -> { + nativeOnFuncNoParamsResult(i, callId);}); + } + + //Should not be called directly, use funcNoParamsAsync(String callId, ) + public CompletableFuture funcNoParamsAsync() + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcNoParamsAsync(); } public boolean funcBool(boolean paramBool) { @@ -446,6 +464,7 @@ public void onSigString(String paramString) private native void nativeOnSigFloat64(double paramFloat64); private native void nativeOnSigString(String paramString); private native void nativeOnFuncNoReturnValueResult(String callId); + private native void nativeOnFuncNoParamsResult(boolean result, String callId); private native void nativeOnFuncBoolResult(boolean result, String callId); private native void nativeOnFuncIntResult(int result, String callId); private native void nativeOnFuncInt32Result(int result, String callId); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java index 7d9a3c9..f3f6c96 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java @@ -163,6 +163,19 @@ public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { executor); } + @Override + public boolean funcNoParams() { + Log.w(TAG, "request method funcNoParams called, will call native"); + return nativeFuncNoParams(); + } + + @Override + public CompletableFuture funcNoParamsAsync() { + return CompletableFuture.supplyAsync( + () -> {return funcNoParams(); }, + executor); + } + @Override public boolean funcBool(boolean paramBool) { Log.w(TAG, "request method funcBool called, will call native"); @@ -299,6 +312,7 @@ public boolean _isReady() { // methods private native void nativeFuncNoReturnValue(boolean paramBool); + private native boolean nativeFuncNoParams(); private native boolean nativeFuncBool(boolean paramBool); private native int nativeFuncInt(int paramInt); private native int nativeFuncInt32(int paramInt32); diff --git a/goldenmaster/testbed1/testbed1_android_client/build.gradle b/goldenmaster/testbed1/testbed1_android_client/build.gradle index 8ebcace..f163874 100644 --- a/goldenmaster/testbed1/testbed1_android_client/build.gradle +++ b/goldenmaster/testbed1/testbed1_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':testbed1_impl') } diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java index 2264b9e..5ed04bb 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java @@ -325,7 +325,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -343,7 +343,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -361,7 +361,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -379,7 +379,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -397,7 +397,7 @@ public void handleMessage(Message msg) case RPC_FuncEnumResp: { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -424,7 +424,8 @@ public void handleMessage(Message msg) public void setPropBool(StructBoolWithArray propBool) { Log.i(TAG, "request setPropBool called "+ propBool); - if (! m_propBool.equals(propBool)) + if ( (m_propBool != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) { Message msg = new Message(); msg.what = StructArray2InterfaceMessageType.PROP_PropBool.getValue(); @@ -440,7 +441,8 @@ public void setPropBool(StructBoolWithArray propBool) public void onPropBool(StructBoolWithArray propBool) { Log.i(TAG, "value received from service for PropBool "); - if (! m_propBool.equals(propBool)) + if ( (m_propBool != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) { m_propBool = propBool; firePropBoolChanged(propBool); @@ -460,7 +462,8 @@ public StructBoolWithArray getPropBool() public void setPropInt(StructIntWithArray propInt) { Log.i(TAG, "request setPropInt called "+ propInt); - if (! m_propInt.equals(propInt)) + if ( (m_propInt != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) { Message msg = new Message(); msg.what = StructArray2InterfaceMessageType.PROP_PropInt.getValue(); @@ -476,7 +479,8 @@ public void setPropInt(StructIntWithArray propInt) public void onPropInt(StructIntWithArray propInt) { Log.i(TAG, "value received from service for PropInt "); - if (! m_propInt.equals(propInt)) + if ( (m_propInt != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) { m_propInt = propInt; firePropIntChanged(propInt); @@ -496,7 +500,8 @@ public StructIntWithArray getPropInt() public void setPropFloat(StructFloatWithArray propFloat) { Log.i(TAG, "request setPropFloat called "+ propFloat); - if (! m_propFloat.equals(propFloat)) + if ( (m_propFloat != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) { Message msg = new Message(); msg.what = StructArray2InterfaceMessageType.PROP_PropFloat.getValue(); @@ -512,7 +517,8 @@ public void setPropFloat(StructFloatWithArray propFloat) public void onPropFloat(StructFloatWithArray propFloat) { Log.i(TAG, "value received from service for PropFloat "); - if (! m_propFloat.equals(propFloat)) + if ( (m_propFloat != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) { m_propFloat = propFloat; firePropFloatChanged(propFloat); @@ -532,7 +538,8 @@ public StructFloatWithArray getPropFloat() public void setPropString(StructStringWithArray propString) { Log.i(TAG, "request setPropString called "+ propString); - if (! m_propString.equals(propString)) + if ( (m_propString != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) { Message msg = new Message(); msg.what = StructArray2InterfaceMessageType.PROP_PropString.getValue(); @@ -548,7 +555,8 @@ public void setPropString(StructStringWithArray propString) public void onPropString(StructStringWithArray propString) { Log.i(TAG, "value received from service for PropString "); - if (! m_propString.equals(propString)) + if ( (m_propString != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) { m_propString = propString; firePropStringChanged(propString); @@ -568,7 +576,8 @@ public StructStringWithArray getPropString() public void setPropEnum(StructEnumWithArray propEnum) { Log.i(TAG, "request setPropEnum called "+ propEnum); - if (! m_propEnum.equals(propEnum)) + if ( (m_propEnum != null && ! m_propEnum.equals(propEnum)) + || (m_propEnum == null && propEnum != null )) { Message msg = new Message(); msg.what = StructArray2InterfaceMessageType.PROP_PropEnum.getValue(); @@ -584,7 +593,8 @@ public void setPropEnum(StructEnumWithArray propEnum) public void onPropEnum(StructEnumWithArray propEnum) { Log.i(TAG, "value received from service for PropEnum "); - if (! m_propEnum.equals(propEnum)) + if ( (m_propEnum != null && ! m_propEnum.equals(propEnum)) + || (m_propEnum == null && propEnum != null )) { m_propEnum = propEnum; firePropEnumChanged(propEnum); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java index f5a59fe..ac6dd39 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java @@ -325,7 +325,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -343,7 +343,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -361,7 +361,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -379,7 +379,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -397,7 +397,7 @@ public void handleMessage(Message msg) case RPC_FuncEnumResp: { Bundle data = msg.getData(); - data.setClassLoader(Enum0Parcelable.class.getClassLoader()); + data.setClassLoader(Enum0Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java index 08165c1..540a21c 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java @@ -298,7 +298,7 @@ public void handleMessage(Message msg) case RPC_FuncBoolResp: { Bundle data = msg.getData(); - data.setClassLoader(StructBoolParcelable.class.getClassLoader()); + data.setClassLoader(StructBoolParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -316,7 +316,7 @@ public void handleMessage(Message msg) case RPC_FuncIntResp: { Bundle data = msg.getData(); - data.setClassLoader(StructIntParcelable.class.getClassLoader()); + data.setClassLoader(StructIntParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -334,7 +334,7 @@ public void handleMessage(Message msg) case RPC_FuncFloatResp: { Bundle data = msg.getData(); - data.setClassLoader(StructFloatParcelable.class.getClassLoader()); + data.setClassLoader(StructFloatParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -352,7 +352,7 @@ public void handleMessage(Message msg) case RPC_FuncStringResp: { Bundle data = msg.getData(); - data.setClassLoader(StructStringParcelable.class.getClassLoader()); + data.setClassLoader(StructStringParcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -379,7 +379,8 @@ public void handleMessage(Message msg) public void setPropBool(StructBool propBool) { Log.i(TAG, "request setPropBool called "+ propBool); - if (! m_propBool.equals(propBool)) + if ( (m_propBool != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) { Message msg = new Message(); msg.what = StructInterfaceMessageType.PROP_PropBool.getValue(); @@ -395,7 +396,8 @@ public void setPropBool(StructBool propBool) public void onPropBool(StructBool propBool) { Log.i(TAG, "value received from service for PropBool "); - if (! m_propBool.equals(propBool)) + if ( (m_propBool != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) { m_propBool = propBool; firePropBoolChanged(propBool); @@ -415,7 +417,8 @@ public StructBool getPropBool() public void setPropInt(StructInt propInt) { Log.i(TAG, "request setPropInt called "+ propInt); - if (! m_propInt.equals(propInt)) + if ( (m_propInt != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) { Message msg = new Message(); msg.what = StructInterfaceMessageType.PROP_PropInt.getValue(); @@ -431,7 +434,8 @@ public void setPropInt(StructInt propInt) public void onPropInt(StructInt propInt) { Log.i(TAG, "value received from service for PropInt "); - if (! m_propInt.equals(propInt)) + if ( (m_propInt != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) { m_propInt = propInt; firePropIntChanged(propInt); @@ -451,7 +455,8 @@ public StructInt getPropInt() public void setPropFloat(StructFloat propFloat) { Log.i(TAG, "request setPropFloat called "+ propFloat); - if (! m_propFloat.equals(propFloat)) + if ( (m_propFloat != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) { Message msg = new Message(); msg.what = StructInterfaceMessageType.PROP_PropFloat.getValue(); @@ -467,7 +472,8 @@ public void setPropFloat(StructFloat propFloat) public void onPropFloat(StructFloat propFloat) { Log.i(TAG, "value received from service for PropFloat "); - if (! m_propFloat.equals(propFloat)) + if ( (m_propFloat != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) { m_propFloat = propFloat; firePropFloatChanged(propFloat); @@ -487,7 +493,8 @@ public StructFloat getPropFloat() public void setPropString(StructString propString) { Log.i(TAG, "request setPropString called "+ propString); - if (! m_propString.equals(propString)) + if ( (m_propString != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) { Message msg = new Message(); msg.what = StructInterfaceMessageType.PROP_PropString.getValue(); @@ -503,7 +510,8 @@ public void setPropString(StructString propString) public void onPropString(StructString propString) { Log.i(TAG, "value received from service for PropString "); - if (! m_propString.equals(propString)) + if ( (m_propString != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) { m_propString = propString; firePropStringChanged(propString); diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java index cce6bfd..02b11ae 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java @@ -31,6 +31,15 @@ import testbed1.testbed1_api.Testbed1TestHelper; import testbed1.testbed1_api.Enum0; import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceParcelable; +import testbed1.testbed1_impl.StructInterfaceService; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceParcelable; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceParcelable; +import testbed1.testbed1_impl.StructArray2InterfaceService; import testbed1.testbed1_api.IStructArray2InterfaceEventListener; import testbed1.testbed1_api.IStructArray2Interface; diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java index aa5081f..30bc747 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java @@ -31,6 +31,15 @@ import testbed1.testbed1_api.Testbed1TestHelper; import testbed1.testbed1_api.Enum0; import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceParcelable; +import testbed1.testbed1_impl.StructInterfaceService; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceParcelable; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceParcelable; +import testbed1.testbed1_impl.StructArray2InterfaceService; import testbed1.testbed1_api.IStructArrayInterfaceEventListener; import testbed1.testbed1_api.IStructArrayInterface; diff --git a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java index 0085dc1..dd33129 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java @@ -31,6 +31,15 @@ import testbed1.testbed1_api.Testbed1TestHelper; import testbed1.testbed1_api.Enum0; import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceParcelable; +import testbed1.testbed1_impl.StructInterfaceService; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceParcelable; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceParcelable; +import testbed1.testbed1_impl.StructArray2InterfaceService; import testbed1.testbed1_api.IStructInterfaceEventListener; import testbed1.testbed1_api.IStructInterface; diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java index a420164..ba36d06 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java @@ -44,6 +44,15 @@ import testbed1.testbed1_api.Testbed1TestHelper; import testbed1.testbed1_api.Enum0; import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceParcelable; +import testbed1.testbed1_impl.StructInterfaceService; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceParcelable; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceParcelable; +import testbed1.testbed1_impl.StructArray2InterfaceService; import testbed1.testbed1_api.IStructArray2InterfaceEventListener; diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java index 1b8e366..47bdd37 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java @@ -44,6 +44,15 @@ import testbed1.testbed1_api.Testbed1TestHelper; import testbed1.testbed1_api.Enum0; import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceParcelable; +import testbed1.testbed1_impl.StructInterfaceService; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceParcelable; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceParcelable; +import testbed1.testbed1_impl.StructArray2InterfaceService; import testbed1.testbed1_api.IStructArrayInterfaceEventListener; diff --git a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java index a7946fc..e1b79d6 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java @@ -44,6 +44,15 @@ import testbed1.testbed1_api.Testbed1TestHelper; import testbed1.testbed1_api.Enum0; import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.IStructInterface; +import testbed1.testbed1_android_messenger.StructInterfaceParcelable; +import testbed1.testbed1_impl.StructInterfaceService; +import testbed1.testbed1_api.IStructArrayInterface; +import testbed1.testbed1_android_messenger.StructArrayInterfaceParcelable; +import testbed1.testbed1_impl.StructArrayInterfaceService; +import testbed1.testbed1_api.IStructArray2Interface; +import testbed1.testbed1_android_messenger.StructArray2InterfaceParcelable; +import testbed1.testbed1_impl.StructArray2InterfaceService; import testbed1.testbed1_api.IStructInterfaceEventListener; diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java index ce3f665..8db9607 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java @@ -48,7 +48,8 @@ public StructArray2InterfaceService() public void setPropBool(StructBoolWithArray propBool) { Log.i(TAG, "request setPropBool called "); - if (! m_propBool.equals(propBool)) + if ( (m_propBool != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) { m_propBool = propBool; onPropBoolChanged(m_propBool); @@ -68,7 +69,8 @@ public StructBoolWithArray getPropBool() public void setPropInt(StructIntWithArray propInt) { Log.i(TAG, "request setPropInt called "); - if (! m_propInt.equals(propInt)) + if ( (m_propInt != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) { m_propInt = propInt; onPropIntChanged(m_propInt); @@ -88,7 +90,8 @@ public StructIntWithArray getPropInt() public void setPropFloat(StructFloatWithArray propFloat) { Log.i(TAG, "request setPropFloat called "); - if (! m_propFloat.equals(propFloat)) + if ( (m_propFloat != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) { m_propFloat = propFloat; onPropFloatChanged(m_propFloat); @@ -108,7 +111,8 @@ public StructFloatWithArray getPropFloat() public void setPropString(StructStringWithArray propString) { Log.i(TAG, "request setPropString called "); - if (! m_propString.equals(propString)) + if ( (m_propString != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) { m_propString = propString; onPropStringChanged(m_propString); @@ -128,7 +132,8 @@ public StructStringWithArray getPropString() public void setPropEnum(StructEnumWithArray propEnum) { Log.i(TAG, "request setPropEnum called "); - if (! m_propEnum.equals(propEnum)) + if ( (m_propEnum != null && ! m_propEnum.equals(propEnum)) + || (m_propEnum == null && propEnum != null )) { m_propEnum = propEnum; onPropEnumChanged(m_propEnum); diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java index bf6dc7b..5a4b95c 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java @@ -41,7 +41,8 @@ public StructInterfaceService() public void setPropBool(StructBool propBool) { Log.i(TAG, "request setPropBool called "); - if (! m_propBool.equals(propBool)) + if ( (m_propBool != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) { m_propBool = propBool; onPropBoolChanged(m_propBool); @@ -61,7 +62,8 @@ public StructBool getPropBool() public void setPropInt(StructInt propInt) { Log.i(TAG, "request setPropInt called "); - if (! m_propInt.equals(propInt)) + if ( (m_propInt != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) { m_propInt = propInt; onPropIntChanged(m_propInt); @@ -81,7 +83,8 @@ public StructInt getPropInt() public void setPropFloat(StructFloat propFloat) { Log.i(TAG, "request setPropFloat called "); - if (! m_propFloat.equals(propFloat)) + if ( (m_propFloat != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) { m_propFloat = propFloat; onPropFloatChanged(m_propFloat); @@ -101,7 +104,8 @@ public StructFloat getPropFloat() public void setPropString(StructString propString) { Log.i(TAG, "request setPropString called "); - if (! m_propString.equals(propString)) + if ( (m_propString != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) { m_propString = propString; onPropStringChanged(m_propString); diff --git a/goldenmaster/testbed2/testbed2_android_client/build.gradle b/goldenmaster/testbed2/testbed2_android_client/build.gradle index 50dec0e..9df8d83 100644 --- a/goldenmaster/testbed2/testbed2_android_client/build.gradle +++ b/goldenmaster/testbed2/testbed2_android_client/build.gradle @@ -32,4 +32,5 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation project(':testbed2_impl') } diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java index 15707d7..a38ad87 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java @@ -214,10 +214,45 @@ public void handleMessage(Message msg) onSig1(param1); break; } + case RPC_FuncNoReturnValueResp: { + + Bundle data = msg.getData(); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueResp , could not find pending call for " + msg.obj); + } + break; + + } + case RPC_FuncNoParamsResp: { + + Bundle data = msg.getData(); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + Consumer foundCall = mpendingCalls.remove(callId); + if (foundCall != null) + { + foundCall.accept(data); + } + else + { + Log.v(TAG, "received NestedStruct1InterfaceMessageType.RPC_FuncNoParamsResp , could not find pending call for " + msg.obj); + } + break; + + } case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -244,7 +279,8 @@ public void handleMessage(Message msg) public void setProp1(NestedStruct1 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = NestedStruct1InterfaceMessageType.PROP_Prop1.getValue(); @@ -260,7 +296,8 @@ public void setProp1(NestedStruct1 prop1) public void onProp1(NestedStruct1 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); @@ -279,6 +316,87 @@ public NestedStruct1 getProp1() // methods + @Override + public void funcNoReturnValue(NestedStruct1 param1) { + CompletableFuture resFuture = funcNoReturnValueAsync(param1); + try { + resFuture.get(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1) { + + Log.i(TAG, "Call on service funcNoReturnValue "+ " " + param1); + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + + data.putParcelable("param1", new NestedStruct1Parcelable(param1)); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + future.complete(null); + Log.v(TAG, "resolve funcNoReturnValue"); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + + @Override + public NestedStruct1 funcNoParams() { + CompletableFuture resFuture = funcNoParamsAsync(); + try { + return resFuture.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public CompletableFuture funcNoParamsAsync() { + + Log.i(TAG, "Call on service funcNoParams "); + Message msg = new Message(); + msg.what = NestedStruct1InterfaceMessageType.RPC_FuncNoParamsReq.getValue(); + Bundle data = new Bundle(); + int msgId = callIdsGetter.getAndIncrement(); + data.putInt("callId",msgId); + msg.setData(data); + msg.replyTo = mClientMessenger; + mClientHandler.sendToService(msg); + + CompletableFuture future = new CompletableFuture<>(); + Consumer resolver = bundle -> { + + NestedStruct1 result = bundle.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + Log.v(TAG, "resolve funcNoParams" + result); + future.complete(result); + }; + + // Store the lambda function in the map + mpendingCalls.put(msgId, resolver); + + return future; + } + + @Override public NestedStruct1 func1(NestedStruct1 param1) { CompletableFuture resFuture = func1Async(param1); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java index 5ec8bbb..71dd1cc 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java @@ -246,7 +246,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -264,7 +264,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -291,7 +291,8 @@ public void handleMessage(Message msg) public void setProp1(NestedStruct1 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = NestedStruct2InterfaceMessageType.PROP_Prop1.getValue(); @@ -307,7 +308,8 @@ public void setProp1(NestedStruct1 prop1) public void onProp1(NestedStruct1 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); @@ -327,7 +329,8 @@ public NestedStruct1 getProp1() public void setProp2(NestedStruct2 prop2) { Log.i(TAG, "request setProp2 called "+ prop2); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { Message msg = new Message(); msg.what = NestedStruct2InterfaceMessageType.PROP_Prop2.getValue(); @@ -343,7 +346,8 @@ public void setProp2(NestedStruct2 prop2) public void onProp2(NestedStruct2 prop2) { Log.i(TAG, "value received from service for Prop2 "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; fireProp2Changed(prop2); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java index 6f1a0a4..4b488ef 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java @@ -277,7 +277,7 @@ public void handleMessage(Message msg) case RPC_Func1Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -295,7 +295,7 @@ public void handleMessage(Message msg) case RPC_Func2Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -313,7 +313,7 @@ public void handleMessage(Message msg) case RPC_Func3Resp: { Bundle data = msg.getData(); - data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); int callId = data.getInt("callId"); Consumer foundCall = mpendingCalls.remove(callId); @@ -340,7 +340,8 @@ public void handleMessage(Message msg) public void setProp1(NestedStruct1 prop1) { Log.i(TAG, "request setProp1 called "+ prop1); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { Message msg = new Message(); msg.what = NestedStruct3InterfaceMessageType.PROP_Prop1.getValue(); @@ -356,7 +357,8 @@ public void setProp1(NestedStruct1 prop1) public void onProp1(NestedStruct1 prop1) { Log.i(TAG, "value received from service for Prop1 "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; fireProp1Changed(prop1); @@ -376,7 +378,8 @@ public NestedStruct1 getProp1() public void setProp2(NestedStruct2 prop2) { Log.i(TAG, "request setProp2 called "+ prop2); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { Message msg = new Message(); msg.what = NestedStruct3InterfaceMessageType.PROP_Prop2.getValue(); @@ -392,7 +395,8 @@ public void setProp2(NestedStruct2 prop2) public void onProp2(NestedStruct2 prop2) { Log.i(TAG, "value received from service for Prop2 "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; fireProp2Changed(prop2); @@ -412,7 +416,8 @@ public NestedStruct2 getProp2() public void setProp3(NestedStruct3 prop3) { Log.i(TAG, "request setProp3 called "+ prop3); - if (! m_prop3.equals(prop3)) + if ( (m_prop3 != null && ! m_prop3.equals(prop3)) + || (m_prop3 == null && prop3 != null )) { Message msg = new Message(); msg.what = NestedStruct3InterfaceMessageType.PROP_Prop3.getValue(); @@ -428,7 +433,8 @@ public void setProp3(NestedStruct3 prop3) public void onProp3(NestedStruct3 prop3) { Log.i(TAG, "value received from service for Prop3 "); - if (! m_prop3.equals(prop3)) + if ( (m_prop3 != null && ! m_prop3.equals(prop3)) + || (m_prop3 == null && prop3 != null )) { m_prop3 = prop3; fireProp3Changed(prop3); diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java index b97d485..723ce93 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java @@ -25,6 +25,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.IManyParamInterfaceEventListener; import testbed2.testbed2_api.IManyParamInterface; diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java index 73898bc..60eb8b4 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java @@ -25,6 +25,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; import testbed2.testbed2_api.INestedStruct1Interface; @@ -215,6 +227,90 @@ public void whenNotifiedsig1() throws RemoteException } + public void onfuncNoReturnValueRequest() throws RemoteException { + + // Execute method + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcNoReturnValueAsync(testparam1); + + resFuture.thenAccept(result -> { + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + + NestedStruct1 receivedparam1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + assertEquals(receivedparam1, testparam1); + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + + public void onfuncNoParamsRequest() throws RemoteException { + + // Execute method + NestedStruct1 expectedResult = Testbed2TestHelper.makeTestNestedStruct1(); + + AtomicBoolean receivedResp = new AtomicBoolean(false); + CompletableFuture resFuture = testedClient.funcNoParamsAsync(); + + resFuture.thenAccept(result -> { + assertEquals(expectedResult, result); + receivedResp.set(true); + }); + Robolectric.flushForegroundThreadScheduler(); + + // Expect msg to be sent. + inOrderServiceMessenger.verify(serviceMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + + + Message method_request = messageCaptor.getValue(); + assertEquals(NestedStruct1InterfaceMessageType.RPC_FuncNoParamsReq.getValue(), method_request.what); + Bundle data = method_request.getData(); + + int returnedCallId = data.getInt("callId", -1); + + //Prepare response + + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_FuncNoParamsResp.getValue()); + + Bundle result_data = new Bundle(); + result_data.putInt("callId", returnedCallId); + result_data.putParcelable("result", new NestedStruct1Parcelable(expectedResult)); + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + + public void onfunc1Request() throws RemoteException { // Execute method diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java index 9e2f8a9..6209281 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java @@ -25,6 +25,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; import testbed2.testbed2_api.INestedStruct2Interface; diff --git a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java index 1e17fdf..ca7e034 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java @@ -25,6 +25,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; import testbed2.testbed2_api.INestedStruct3Interface; diff --git a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java index 943e958..15bd2a9 100644 --- a/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java @@ -7,8 +7,12 @@ public enum NestedStruct1InterfaceMessageType { PROP_Prop1(3), SET_Prop1(4), SIG_Sig1(5), - RPC_Func1Req(6), - RPC_Func1Resp(7), + RPC_FuncNoReturnValueReq(6), + RPC_FuncNoReturnValueResp(7), + RPC_FuncNoParamsReq(8), + RPC_FuncNoParamsResp(9), + RPC_Func1Req(10), + RPC_Func1Resp(11), NestedStruct1InterfaceMessageType_UNKNOWN(Integer.MAX_VALUE); private final int value; diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java index 8a53d89..2767739 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java @@ -215,6 +215,61 @@ public void handleMessage(Message msg) } // TODO params may be different structs from different modules, there should be a custom class loader // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncNoReturnValueReq: { + + Bundle data = msg.getData(); + + data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + int callId = data.getInt("callId"); + + NestedStruct1 param1 = data.getParcelable("param1", NestedStruct1Parcelable.class).getNestedStruct1(); + + mBackendService.funcNoReturnValue(param1); + + Message respMsg = new Message(); + respMsg.what = NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message + // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. + case RPC_FuncNoParamsReq: { + + Bundle data = msg.getData(); + + int callId = data.getInt("callId"); + + NestedStruct1 result = mBackendService.funcNoParams(); + + Message respMsg = new Message(); + respMsg.what = NestedStruct1InterfaceMessageType.RPC_FuncNoParamsResp.getValue(); + Bundle resp_data = new Bundle(); + resp_data.putInt("callId", callId); + + resp_data.putParcelable("result", new NestedStruct1Parcelable(result)); + respMsg.setData(resp_data); + + try { + msg.replyTo.send(respMsg); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + break; + + } + // TODO params may be different structs from different modules, there should be a custom class loader + // with a list of class loaders required for this message // IF there are at least 2 different structs from different modules - in theory if it is from same module setting loader for one should work for all structs from this module. case RPC_Func1Req: { diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java index eddc220..0d2eaf3 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java @@ -38,6 +38,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.IManyParamInterfaceEventListener; diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java index c54a3a7..c0be37a 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java @@ -38,6 +38,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; @@ -273,6 +285,64 @@ public void whenNotifiedsig1() } + public void onfuncNoReturnValueRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 testparam1 = Testbed2TestHelper.makeTestNestedStruct1(); + data.putParcelable("param1", new NestedStruct1Parcelable(testparam1)); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcNoReturnValue( any(NestedStruct1.class)); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.RPC_FuncNoReturnValueResp.getValue(), response.what); + Bundle resp_data = response.getData(); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + + public void onfuncNoParamsRequest() throws RemoteException { + // Create and send message + Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_FuncNoParamsReq.getValue()); + Bundle data = new Bundle(); + + int callId = 99; + data.putInt("callId", callId); + NestedStruct1 returnedValue = Testbed2TestHelper.makeTestNestedStruct1(); + + + when(backendServiceMock.funcNoParams()).thenReturn(returnedValue); + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + inOrderBackendService.verify(backendServiceMock,times(1)).funcNoParams(); + + //Now verify it was sent back to caller + Robolectric.flushForegroundThreadScheduler(); + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals(NestedStruct1InterfaceMessageType.RPC_FuncNoParamsResp.getValue(), response.what); + Bundle resp_data = response.getData(); + resp_data.setClassLoader(NestedStruct1Parcelable.class.getClassLoader()); + NestedStruct1 receivedByClient = resp_data.getParcelable("result", NestedStruct1Parcelable.class).getNestedStruct1(); + + assertEquals(receivedByClient, returnedValue); + assertEquals(callId, resp_data.getInt("callId", -1)); + } + + public void onfunc1Request() throws RemoteException { // Create and send message Message msg = Message.obtain(null, NestedStruct1InterfaceMessageType.RPC_Func1Req.getValue()); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java index 800ed2e..0c04fcf 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java @@ -38,6 +38,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; diff --git a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java index dda8c61..f7d382f 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java @@ -38,6 +38,18 @@ import testbed2.testbed2_android_messenger.Enum2Parcelable; import testbed2.testbed2_api.Enum3; import testbed2.testbed2_android_messenger.Enum3Parcelable; +import testbed2.testbed2_api.IManyParamInterface; +import testbed2.testbed2_android_messenger.ManyParamInterfaceParcelable; +import testbed2.testbed2_impl.ManyParamInterfaceService; +import testbed2.testbed2_api.INestedStruct1Interface; +import testbed2.testbed2_android_messenger.NestedStruct1InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct1InterfaceService; +import testbed2.testbed2_api.INestedStruct2Interface; +import testbed2.testbed2_android_messenger.NestedStruct2InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct2InterfaceService; +import testbed2.testbed2_api.INestedStruct3Interface; +import testbed2.testbed2_android_messenger.NestedStruct3InterfaceParcelable; +import testbed2.testbed2_impl.NestedStruct3InterfaceService; import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; diff --git a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java index 6f7e76c..5c0357e 100644 --- a/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java @@ -22,6 +22,10 @@ public interface INestedStruct1Interface { void fireProp1Changed(NestedStruct1 newValue); // methods + void funcNoReturnValue(NestedStruct1 param1); + CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1); + NestedStruct1 funcNoParams(); + CompletableFuture funcNoParamsAsync(); NestedStruct1 func1(NestedStruct1 param1); CompletableFuture func1Async(NestedStruct1 param1); public void fireSig1(NestedStruct1 param1); diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java index 88b904d..50db8ed 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java @@ -35,7 +35,8 @@ public NestedStruct1InterfaceService() public void setProp1(NestedStruct1 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); @@ -53,6 +54,32 @@ public NestedStruct1 getProp1() // methods + @Override + public void funcNoReturnValue(NestedStruct1 param1) { + Log.w(TAG, "request method funcNoReturnValue called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1) { + return CompletableFuture.runAsync( + () -> { funcNoReturnValue(param1); }, + executor); + } + + @Override + public NestedStruct1 funcNoParams() { + Log.w(TAG, "request method funcNoParams called, returnig default"); + return new NestedStruct1(); + } + + @Override + public CompletableFuture funcNoParamsAsync() { + return CompletableFuture.supplyAsync( + () -> {return funcNoParams(); }, + executor); + } + @Override public NestedStruct1 func1(NestedStruct1 param1) { Log.w(TAG, "request method func1 called, returnig default"); diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java index 1afd134..07d810d 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java @@ -37,7 +37,8 @@ public NestedStruct2InterfaceService() public void setProp1(NestedStruct1 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); @@ -57,7 +58,8 @@ public NestedStruct1 getProp1() public void setProp2(NestedStruct2 prop2) { Log.i(TAG, "request setProp2 called "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; onProp2Changed(m_prop2); diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java index a425581..351ab75 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java @@ -39,7 +39,8 @@ public NestedStruct3InterfaceService() public void setProp1(NestedStruct1 prop1) { Log.i(TAG, "request setProp1 called "); - if (! m_prop1.equals(prop1)) + if ( (m_prop1 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) { m_prop1 = prop1; onProp1Changed(m_prop1); @@ -59,7 +60,8 @@ public NestedStruct1 getProp1() public void setProp2(NestedStruct2 prop2) { Log.i(TAG, "request setProp2 called "); - if (! m_prop2.equals(prop2)) + if ( (m_prop2 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) { m_prop2 = prop2; onProp2Changed(m_prop2); @@ -79,7 +81,8 @@ public NestedStruct2 getProp2() public void setProp3(NestedStruct3 prop3) { Log.i(TAG, "request setProp3 called "); - if (! m_prop3.equals(prop3)) + if ( (m_prop3 != null && ! m_prop3.equals(prop3)) + || (m_prop3 == null && prop3 != null )) { m_prop3 = prop3; onProp3Changed(m_prop3); diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java index 2924717..636e01a 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -44,6 +44,42 @@ public NestedStruct1 getProp1() return mMessengerClient.getProp1(); } + public void funcNoReturnValue(NestedStruct1 param1) + { + Log.v(TAG, "Blocking callfuncNoReturnValue - should not be used "); + mMessengerClient.funcNoReturnValue(param1); + } + + public void funcNoReturnValueAsync(String callId, NestedStruct1 param1){ + Log.v(TAG, "non blocking call funcNoReturnValue "); + mMessengerClient.funcNoReturnValueAsync(param1).thenAccept(i -> { + nativeOnFuncNoReturnValueResult(callId);}); + } + + //Should not be called directly, use funcNoReturnValueAsync(String callId, NestedStruct1 param1) + public CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1) + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcNoReturnValueAsync(param1); + } + public NestedStruct1 funcNoParams() + { + Log.v(TAG, "Blocking callfuncNoParams - should not be used "); + return mMessengerClient.funcNoParams(); + } + + public void funcNoParamsAsync(String callId){ + Log.v(TAG, "non blocking call funcNoParams "); + mMessengerClient.funcNoParamsAsync().thenAccept(i -> { + nativeOnFuncNoParamsResult(i, callId);}); + } + + //Should not be called directly, use funcNoParamsAsync(String callId, ) + public CompletableFuture funcNoParamsAsync() + { + Log.v(TAG, "NON Blocking call method "); + return mMessengerClient.funcNoParamsAsync(); + } public NestedStruct1 func1(NestedStruct1 param1) { Log.v(TAG, "Blocking callfunc1 - should not be used "); @@ -114,6 +150,8 @@ public void onSig1(NestedStruct1 param1) } private native void nativeOnProp1Changed(NestedStruct1 prop1); private native void nativeOnSig1(NestedStruct1 param1); + private native void nativeOnFuncNoReturnValueResult(String callId); + private native void nativeOnFuncNoParamsResult(NestedStruct1 result, String callId); private native void nativeOnFunc1Result(NestedStruct1 result, String callId); private native void nativeIsReady(boolean isReady); } diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java index 30b94a6..3a18bd8 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java @@ -47,6 +47,32 @@ public NestedStruct1 getProp1() // methods + @Override + public void funcNoReturnValue(NestedStruct1 param1) { + Log.w(TAG, "request method funcNoReturnValue called, will call native"); + nativeFuncNoReturnValue(param1); + } + + @Override + public CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1) { + return CompletableFuture.runAsync( + () -> { funcNoReturnValue(param1); }, + executor); + } + + @Override + public NestedStruct1 funcNoParams() { + Log.w(TAG, "request method funcNoParams called, will call native"); + return nativeFuncNoParams(); + } + + @Override + public CompletableFuture funcNoParamsAsync() { + return CompletableFuture.supplyAsync( + () -> {return funcNoParams(); }, + executor); + } + @Override public NestedStruct1 func1(NestedStruct1 param1) { Log.w(TAG, "request method func1 called, will call native"); @@ -70,6 +96,8 @@ public boolean _isReady() { private native NestedStruct1 nativeGetProp1(); // methods + private native void nativeFuncNoReturnValue(NestedStruct1 param1); + private native NestedStruct1 nativeFuncNoParams(); private native NestedStruct1 nativeFunc1(NestedStruct1 param1); // Called by Native Impl Service diff --git a/templates/android/client/build.gradle.tpl b/templates/android/client/build.gradle.tpl index 6f6d0ee..08706f0 100644 --- a/templates/android/client/build.gradle.tpl +++ b/templates/android/client/build.gradle.tpl @@ -35,4 +35,13 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.mockito:mockito-inline:5.2.0' + {{- if len (.Module.Interfaces)}} + testImplementation project(':{{camel .Module.Name}}_impl') + {{- end }} + {{- range .Module.Imports}} + {{- $importModule := ($.System.LookupModule .Name) }} + {{- if len $importModule.Interfaces}} + testImplementation '{{camel .Name}}:{{camel .Name}}_impl:{{ $importModule.Version }}' + {{- end }} + {{- end }} } diff --git a/templates/android/client/clienttest.java.tpl b/templates/android/client/clienttest.java.tpl index 0a650cb..432954b 100644 --- a/templates/android/client/clienttest.java.tpl +++ b/templates/android/client/clienttest.java.tpl @@ -13,6 +13,11 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Module.Name}}T import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} +{{- range .Module.Interfaces }} +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_impl.{{Camel .Name}}Service; +{{- end }} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}; @@ -89,7 +94,7 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter {{- if (eq .KindType "extern") }} test{{ javaVar .}}[0] = {{javaTestValue "" .}}; {{- else }} - test{{ javaVar .}}[0] = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaDefault "" .}}{{end}}); + test{{ javaVar .}}[0] = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaTestValue "" .}}{{end}}); {{- end }} {{- end}} {{- else if or (.IsPrimitive) (eq .KindType "enum") }} @@ -97,7 +102,7 @@ interface I{{Camel .Interface.Name }}ClientMessageGetter {{- else if (eq .KindType "extern") }} {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" .}}; {{- else }} - {{javaReturn "" . }} test{{ javaVar .}} = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaDefault "" .}}{{end}}); + {{javaReturn "" . }} test{{ javaVar .}} = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaTestValue "" .}}{{end}}); {{- end }} {{- end }} @@ -306,7 +311,7 @@ public class {{Camel .Interface.Name }}ClientTest {{- if (eq .Return.KindType "extern") }} expectedResult[0] = {{javaTestValue "" .Return}}; {{- else }} - expectedResult[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaDefault "" .Return}}{{end}}); + expectedResult[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaTestValue "" .Return}}{{end}}); {{- end }} {{- end}} {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} diff --git a/templates/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl index 69bb8ae..e2da318 100644 --- a/templates/android/service/serviceadaptertest.java.tpl +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -27,6 +27,11 @@ import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Module.Name}}T import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; {{- end }} +{{- range .Module.Interfaces }} +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Name}}; +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger.{{Camel .Name}}Parcelable; +import {{camel .Module.Name}}.{{camel .Module.Name}}_impl.{{Camel .Name}}Service; +{{- end }} import {{camel .Module.Name}}.{{camel .Module.Name}}_api.I{{Camel .Interface.Name }}EventListener; @@ -338,7 +343,7 @@ public class {{Camel .Interface.Name }}ServiceAdapterTest {{- if (eq .Return.KindType "extern") }} returnedValue[0] = {{javaTestValue "" .Return}}; {{- else }} - returnedValue[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaDefault "" .Return}}{{end}}); + returnedValue[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaTestValue "" .Return}}{{end}}); {{- end }} {{- end}} {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} From cc6f3d0904c947b45e066f6f0223791678e9c60f Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:04:16 +0100 Subject: [PATCH 42/45] fix: clean up log levels --- .../counter_android_client/CounterClient.java | 2 +- .../CounterServiceFactory.java | 4 +- .../CounterServiceStarter.java | 8 ++-- .../CounterTestClientApp.java | 26 ++++++------- .../counter/counter_impl/CounterService.java | 8 ++-- .../counterjniclient/CounterJniClient.java | 14 +++---- .../counterjniservice/CounterJniService.java | 8 ++-- .../CounterJniServiceFactory.java | 4 +- .../CounterJniServiceStarter.java | 8 ++-- .../CounterTestServiceApp.java | 19 +++++----- .../EnumInterfaceClient.java | 2 +- .../EnumInterfaceServiceFactory.java | 4 +- .../EnumInterfaceServiceStarter.java | 8 ++-- .../TbEnumTestClientApp.java | 32 ++++++++-------- .../tbEnum_impl/EnumInterfaceService.java | 8 ++-- .../EnumInterfaceJniClient.java | 20 +++++----- .../EnumInterfaceJniService.java | 8 ++-- .../EnumInterfaceJniServiceFactory.java | 4 +- .../EnumInterfaceJniServiceStarter.java | 8 ++-- .../TbEnumTestServiceApp.java | 31 ++++++++------- .../EmptyIfClient.java | 2 +- .../EmptyIfServiceFactory.java | 4 +- .../EmptyIfServiceStarter.java | 8 ++-- .../TbIfaceimportTestClientApp.java | 8 ++-- .../EmptyIfJniClient.java | 4 +- .../EmptyIfJniServiceFactory.java | 4 +- .../EmptyIfJniServiceStarter.java | 8 ++-- .../TbIfaceimportTestServiceApp.java | 7 ++-- .../tbNames_android_client/NamEsClient.java | 2 +- .../NamEsServiceFactory.java | 4 +- .../NamEsServiceStarter.java | 8 ++-- .../TbNamesTestClientApp.java | 24 ++++++------ .../tbNames/tbNames_impl/NamEsService.java | 4 +- .../tbNamesjniclient/NamEsJniClient.java | 16 ++++---- .../tbNamesjniservice/NamEsJniService.java | 4 +- .../NamEsJniServiceFactory.java | 4 +- .../NamEsJniServiceStarter.java | 8 ++-- .../TbNamesTestServiceApp.java | 23 ++++++----- .../ParentIfClient.java | 2 +- .../SimpleLocalIfClient.java | 2 +- .../ParentIfServiceFactory.java | 4 +- .../ParentIfServiceStarter.java | 8 ++-- .../SimpleLocalIfServiceFactory.java | 4 +- .../SimpleLocalIfServiceStarter.java | 8 ++-- .../TbRefIfacesTestClientApp.java | 14 +++---- .../tbRefIfaces_impl/ParentIfService.java | 8 ++-- .../SimpleLocalIfService.java | 2 +- .../ParentIfJniClient.java | 20 +++++----- .../SimpleLocalIfJniClient.java | 8 ++-- .../ParentIfJniService.java | 8 ++-- .../ParentIfJniServiceFactory.java | 4 +- .../ParentIfJniServiceStarter.java | 8 ++-- .../SimpleLocalIfJniService.java | 2 +- .../SimpleLocalIfJniServiceFactory.java | 4 +- .../SimpleLocalIfJniServiceStarter.java | 8 ++-- .../TbRefIfacesTestServiceApp.java | 13 +++---- .../SameEnum1InterfaceClient.java | 2 +- .../SameEnum2InterfaceClient.java | 2 +- .../SameStruct1InterfaceClient.java | 2 +- .../SameStruct2InterfaceClient.java | 2 +- .../SameEnum1InterfaceServiceFactory.java | 4 +- .../SameEnum1InterfaceServiceStarter.java | 8 ++-- .../SameEnum2InterfaceServiceFactory.java | 4 +- .../SameEnum2InterfaceServiceStarter.java | 8 ++-- .../SameStruct1InterfaceServiceFactory.java | 4 +- .../SameStruct1InterfaceServiceStarter.java | 8 ++-- .../SameStruct2InterfaceServiceFactory.java | 4 +- .../SameStruct2InterfaceServiceStarter.java | 8 ++-- .../TbSame1TestClientApp.java | 14 +++---- .../SameEnum1InterfaceService.java | 2 +- .../SameEnum2InterfaceService.java | 4 +- .../SameStruct1InterfaceService.java | 2 +- .../SameStruct2InterfaceService.java | 4 +- .../SameEnum1InterfaceJniClient.java | 8 ++-- .../SameEnum2InterfaceJniClient.java | 12 +++--- .../SameStruct1InterfaceJniClient.java | 8 ++-- .../SameStruct2InterfaceJniClient.java | 12 +++--- .../SameEnum1InterfaceJniService.java | 2 +- .../SameEnum1InterfaceJniServiceFactory.java | 4 +- .../SameEnum1InterfaceJniServiceStarter.java | 8 ++-- .../SameEnum2InterfaceJniService.java | 4 +- .../SameEnum2InterfaceJniServiceFactory.java | 4 +- .../SameEnum2InterfaceJniServiceStarter.java | 8 ++-- .../SameStruct1InterfaceJniService.java | 2 +- ...SameStruct1InterfaceJniServiceFactory.java | 4 +- ...SameStruct1InterfaceJniServiceStarter.java | 8 ++-- .../SameStruct2InterfaceJniService.java | 4 +- ...SameStruct2InterfaceJniServiceFactory.java | 4 +- ...SameStruct2InterfaceJniServiceStarter.java | 8 ++-- .../TbSame1TestServiceApp.java | 13 +++---- .../SameEnum1InterfaceClient.java | 2 +- .../SameEnum2InterfaceClient.java | 2 +- .../SameStruct1InterfaceClient.java | 2 +- .../SameStruct2InterfaceClient.java | 2 +- .../SameEnum1InterfaceServiceFactory.java | 4 +- .../SameEnum1InterfaceServiceStarter.java | 8 ++-- .../SameEnum2InterfaceServiceFactory.java | 4 +- .../SameEnum2InterfaceServiceStarter.java | 8 ++-- .../SameStruct1InterfaceServiceFactory.java | 4 +- .../SameStruct1InterfaceServiceStarter.java | 8 ++-- .../SameStruct2InterfaceServiceFactory.java | 4 +- .../SameStruct2InterfaceServiceStarter.java | 8 ++-- .../TbSame2TestClientApp.java | 14 +++---- .../SameEnum1InterfaceService.java | 2 +- .../SameEnum2InterfaceService.java | 4 +- .../SameStruct1InterfaceService.java | 2 +- .../SameStruct2InterfaceService.java | 4 +- .../SameEnum1InterfaceJniClient.java | 8 ++-- .../SameEnum2InterfaceJniClient.java | 12 +++--- .../SameStruct1InterfaceJniClient.java | 8 ++-- .../SameStruct2InterfaceJniClient.java | 12 +++--- .../SameEnum1InterfaceJniService.java | 2 +- .../SameEnum1InterfaceJniServiceFactory.java | 4 +- .../SameEnum1InterfaceJniServiceStarter.java | 8 ++-- .../SameEnum2InterfaceJniService.java | 4 +- .../SameEnum2InterfaceJniServiceFactory.java | 4 +- .../SameEnum2InterfaceJniServiceStarter.java | 8 ++-- .../SameStruct1InterfaceJniService.java | 2 +- ...SameStruct1InterfaceJniServiceFactory.java | 4 +- ...SameStruct1InterfaceJniServiceStarter.java | 8 ++-- .../SameStruct2InterfaceJniService.java | 4 +- ...SameStruct2InterfaceJniServiceFactory.java | 4 +- ...SameStruct2InterfaceJniServiceStarter.java | 8 ++-- .../TbSame2TestServiceApp.java | 13 +++---- .../EmptyInterfaceClient.java | 2 +- .../NoOperationsInterfaceClient.java | 2 +- .../NoPropertiesInterfaceClient.java | 2 +- .../NoSignalsInterfaceClient.java | 2 +- .../SimpleArrayInterfaceClient.java | 2 +- .../SimpleInterfaceClient.java | 2 +- .../VoidInterfaceClient.java | 2 +- .../EmptyInterfaceServiceFactory.java | 4 +- .../EmptyInterfaceServiceStarter.java | 8 ++-- .../NoOperationsInterfaceServiceFactory.java | 4 +- .../NoOperationsInterfaceServiceStarter.java | 8 ++-- .../NoPropertiesInterfaceServiceFactory.java | 4 +- .../NoPropertiesInterfaceServiceStarter.java | 8 ++-- .../NoSignalsInterfaceServiceFactory.java | 4 +- .../NoSignalsInterfaceServiceStarter.java | 8 ++-- .../SimpleArrayInterfaceServiceFactory.java | 4 +- .../SimpleArrayInterfaceServiceStarter.java | 8 ++-- .../SimpleInterfaceServiceFactory.java | 4 +- .../SimpleInterfaceServiceStarter.java | 8 ++-- .../VoidInterfaceServiceFactory.java | 4 +- .../VoidInterfaceServiceStarter.java | 8 ++-- .../TbSimpleTestClientApp.java | 12 +++--- .../NoPropertiesInterfaceService.java | 4 +- .../NoSignalsInterfaceService.java | 4 +- .../SimpleArrayInterfaceService.java | 16 ++++---- .../tbSimple_impl/SimpleInterfaceService.java | 20 +++++----- .../tbSimple_impl/VoidInterfaceService.java | 2 +- .../EmptyInterfaceJniClient.java | 4 +- .../NoOperationsInterfaceJniClient.java | 12 +++--- .../NoPropertiesInterfaceJniClient.java | 8 ++-- .../NoSignalsInterfaceJniClient.java | 8 ++-- .../SimpleArrayInterfaceJniClient.java | 38 +++++++++---------- .../SimpleInterfaceJniClient.java | 36 +++++++++--------- .../VoidInterfaceJniClient.java | 6 +-- .../EmptyInterfaceJniServiceFactory.java | 4 +- .../EmptyInterfaceJniServiceStarter.java | 8 ++-- ...oOperationsInterfaceJniServiceFactory.java | 4 +- ...oOperationsInterfaceJniServiceStarter.java | 8 ++-- .../NoPropertiesInterfaceJniService.java | 4 +- ...oPropertiesInterfaceJniServiceFactory.java | 4 +- ...oPropertiesInterfaceJniServiceStarter.java | 8 ++-- .../NoSignalsInterfaceJniService.java | 4 +- .../NoSignalsInterfaceJniServiceFactory.java | 4 +- .../NoSignalsInterfaceJniServiceStarter.java | 8 ++-- .../SimpleArrayInterfaceJniService.java | 16 ++++---- ...SimpleArrayInterfaceJniServiceFactory.java | 4 +- ...SimpleArrayInterfaceJniServiceStarter.java | 8 ++-- .../SimpleInterfaceJniService.java | 20 +++++----- .../SimpleInterfaceJniServiceFactory.java | 4 +- .../SimpleInterfaceJniServiceStarter.java | 8 ++-- .../VoidInterfaceJniService.java | 2 +- .../VoidInterfaceJniServiceFactory.java | 4 +- .../VoidInterfaceJniServiceStarter.java | 8 ++-- .../TbSimpleTestServiceApp.java | 11 +++--- .../StructArray2InterfaceClient.java | 2 +- .../StructArrayInterfaceClient.java | 2 +- .../StructInterfaceClient.java | 2 +- .../StructArray2InterfaceServiceFactory.java | 4 +- .../StructArray2InterfaceServiceStarter.java | 8 ++-- .../StructArrayInterfaceServiceFactory.java | 4 +- .../StructArrayInterfaceServiceStarter.java | 8 ++-- .../StructInterfaceServiceFactory.java | 4 +- .../StructInterfaceServiceStarter.java | 8 ++-- .../Testbed1TestClientApp.java | 32 ++++++++-------- .../StructArray2InterfaceService.java | 10 ++--- .../StructArrayInterfaceService.java | 10 ++--- .../testbed1_impl/StructInterfaceService.java | 8 ++-- .../StructArray2InterfaceJniClient.java | 22 +++++------ .../StructArrayInterfaceJniClient.java | 24 ++++++------ .../StructInterfaceJniClient.java | 20 +++++----- .../StructArray2InterfaceJniService.java | 10 ++--- ...tructArray2InterfaceJniServiceFactory.java | 4 +- ...tructArray2InterfaceJniServiceStarter.java | 8 ++-- .../StructArrayInterfaceJniService.java | 10 ++--- ...StructArrayInterfaceJniServiceFactory.java | 4 +- ...StructArrayInterfaceJniServiceStarter.java | 8 ++-- .../StructInterfaceJniService.java | 8 ++-- .../StructInterfaceJniServiceFactory.java | 4 +- .../StructInterfaceJniServiceStarter.java | 8 ++-- .../Testbed1TestServiceApp.java | 31 ++++++++------- .../ManyParamInterfaceClient.java | 2 +- .../NestedStruct1InterfaceClient.java | 2 +- .../NestedStruct2InterfaceClient.java | 2 +- .../NestedStruct3InterfaceClient.java | 2 +- .../ManyParamInterfaceServiceFactory.java | 4 +- .../ManyParamInterfaceServiceStarter.java | 8 ++-- .../NestedStruct1InterfaceServiceFactory.java | 4 +- .../NestedStruct1InterfaceServiceStarter.java | 8 ++-- .../NestedStruct2InterfaceServiceFactory.java | 4 +- .../NestedStruct2InterfaceServiceStarter.java | 8 ++-- .../NestedStruct3InterfaceServiceFactory.java | 4 +- .../NestedStruct3InterfaceServiceStarter.java | 8 ++-- .../Testbed2TestClientApp.java | 32 ++++++++-------- .../ManyParamInterfaceService.java | 8 ++-- .../NestedStruct1InterfaceService.java | 6 +-- .../NestedStruct2InterfaceService.java | 4 +- .../NestedStruct3InterfaceService.java | 6 +-- .../ManyParamInterfaceJniClient.java | 20 +++++----- .../NestedStruct1InterfaceJniClient.java | 8 ++-- .../NestedStruct2InterfaceJniClient.java | 12 +++--- .../NestedStruct3InterfaceJniClient.java | 16 ++++---- .../ManyParamInterfaceJniService.java | 8 ++-- .../ManyParamInterfaceJniServiceFactory.java | 4 +- .../ManyParamInterfaceJniServiceStarter.java | 8 ++-- .../NestedStruct1InterfaceJniService.java | 6 +-- ...stedStruct1InterfaceJniServiceFactory.java | 4 +- ...stedStruct1InterfaceJniServiceStarter.java | 8 ++-- .../NestedStruct2InterfaceJniService.java | 4 +- ...stedStruct2InterfaceJniServiceFactory.java | 4 +- ...stedStruct2InterfaceJniServiceStarter.java | 8 ++-- .../NestedStruct3InterfaceJniService.java | 6 +-- ...stedStruct3InterfaceJniServiceFactory.java | 4 +- ...stedStruct3InterfaceJniServiceStarter.java | 8 ++-- .../Testbed2TestServiceApp.java | 31 ++++++++------- templates/android/client/client.java.tpl | 2 +- .../service/implservicefactory.java.tpl | 4 +- .../service/implservicestarter.java.tpl | 8 ++-- .../jnibridge/client/jnibridgeclient.java.tpl | 8 ++-- .../service/jnibridgeservice.java.tpl | 2 +- .../service/jnibridgeservicefactory.java.tpl | 4 +- .../service/jnibridgeservicestarter.java.tpl | 8 ++-- templates/stub/implservice.java.tpl | 2 +- templates/testclientapp/application.java.tpl | 14 +++---- templates/testserviceapp/application.java.tpl | 13 +++---- 248 files changed, 941 insertions(+), 952 deletions(-) diff --git a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java index 106ec89..bfbfcbd 100644 --- a/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java +++ b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java @@ -121,7 +121,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java index 322d527..0cd05b7 100644 --- a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: CounterServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: CounterServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private CounterServiceFactory() @NonNull private static CounterServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); CounterServiceFactory t = new CounterServiceFactory(); t.start(); diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java index 93956f5..60d1d15 100644 --- a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceStarter.java @@ -21,11 +21,11 @@ public class CounterServiceStarter { public static ICounter start(Context context) { stop(context); androidService = new Intent(context, CounterServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); CounterServiceFactory factory = CounterServiceFactory.get(); - Log.w(TAG, "starter: factory set for CounterServiceFactory"); + Log.i(TAG, "starter: factory set for CounterServiceFactory"); return CounterServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java b/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java index 28e2177..f67333b 100644 --- a/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java +++ b/goldenmaster/counter/counter_client_example/src/main/java/counter/counter_client_example/CounterTestClientApp.java @@ -111,7 +111,7 @@ protected void onCreate(Bundle savedInstanceState) { bIncrement.setText("increment"); bIncrement.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD increment "); + Log.i(TAG, "CALLING METHOD increment "); org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); CompletableFuture method_res = mClient.incrementAsync(vec).thenApply( @@ -126,7 +126,7 @@ protected void onCreate(Bundle savedInstanceState) { bIncrementArray.setText("incrementArray"); bIncrementArray.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD incrementArray "); + Log.i(TAG, "CALLING METHOD incrementArray "); org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] vec = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); CompletableFuture method_res = mClient.incrementArrayAsync(vec).thenApply( @@ -141,7 +141,7 @@ protected void onCreate(Bundle savedInstanceState) { bDecrement.setText("decrement"); bDecrement.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD decrement "); + Log.i(TAG, "CALLING METHOD decrement "); customTypes.customTypes_api.Vector3D vec = new customTypes.customTypes_api.Vector3D(); CompletableFuture method_res = mClient.decrementAsync(vec).thenApply( @@ -156,7 +156,7 @@ protected void onCreate(Bundle savedInstanceState) { bDecrementArray.setText("decrementArray"); bDecrementArray.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD decrementArray "); + Log.i(TAG, "CALLING METHOD decrementArray "); customTypes.customTypes_api.Vector3D[] vec = new customTypes.customTypes_api.Vector3D(); CompletableFuture method_res = mClient.decrementArrayAsync(vec).thenApply( @@ -262,12 +262,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new CounterClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -279,43 +279,43 @@ private void initServiceConnection( String servicePackage) public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) { outputTextViewProp.setText("Property from service: vector " + newValue); - Log.w(TAG, "Property from service: vector " + newValue); + Log.i(TAG, "Property from service: vector " + newValue); } @Override public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) { outputTextViewProp.setText("Property from service: extern_vector " + newValue); - Log.w(TAG, "Property from service: extern_vector " + newValue); + Log.i(TAG, "Property from service: extern_vector " + newValue); } @Override public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) { outputTextViewProp.setText("Property from service: vectorArray " + newValue); - Log.w(TAG, "Property from service: vectorArray " + newValue); + Log.i(TAG, "Property from service: vectorArray " + newValue); } @Override public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) { outputTextViewProp.setText("Property from service: extern_vectorArray " + newValue); - Log.w(TAG, "Property from service: extern_vectorArray " + newValue); + Log.i(TAG, "Property from service: extern_vectorArray " + newValue); } @Override public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) { String text = "Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java index 30bab8c..e4fd450 100644 --- a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java +++ b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java @@ -119,7 +119,7 @@ public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVe @Override public org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { - Log.w(TAG, "request method increment called, returnig default"); + Log.i(TAG, "request method increment called, returnig default"); return new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); } @@ -132,7 +132,7 @@ public CompletableFuture decrementAsync(c @Override public customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec) { - Log.w(TAG, "request method decrementArray called, returnig default"); + Log.i(TAG, "request method decrementArray called, returnig default"); return new customTypes.customTypes_api.Vector3D[]{}; } diff --git a/goldenmaster/counter/counterjniclient/CounterJniClient.java b/goldenmaster/counter/counterjniclient/CounterJniClient.java index 5cf9c63..0205bd6 100644 --- a/goldenmaster/counter/counterjniclient/CounterJniClient.java +++ b/goldenmaster/counter/counterjniclient/CounterJniClient.java @@ -172,7 +172,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new CounterClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -186,7 +186,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -194,31 +194,31 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnVectorChanged(newValue); } @Override public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnExternVectorChanged(newValue); } @Override public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnVectorArrayChanged(newValue); } @Override public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnExternVectorArrayChanged(newValue); } @Override public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) { - Log.w(TAG, "NOTIFICATION from messenger client Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray); + Log.i(TAG, "NOTIFICATION from messenger client Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray); nativeOnValueChanged(vector, extern_vector, vectorArray, extern_vectorArray); } private native void nativeOnVectorChanged(customTypes.customTypes_api.Vector3D vector); diff --git a/goldenmaster/counter/counterjniservice/CounterJniService.java b/goldenmaster/counter/counterjniservice/CounterJniService.java index bf9802a..7af6e1d 100644 --- a/goldenmaster/counter/counterjniservice/CounterJniService.java +++ b/goldenmaster/counter/counterjniservice/CounterJniService.java @@ -92,7 +92,7 @@ public org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] getExternVe @Override public org.apache.commons.math3.geometry.euclidean.threed.Vector3D increment(org.apache.commons.math3.geometry.euclidean.threed.Vector3D vec) { - Log.w(TAG, "request method increment called, will call native"); + Log.i(TAG, "request method increment called, will call native"); return nativeIncrement(vec); } @@ -105,7 +105,7 @@ public CompletableFuture decrementAsync(c @Override public customTypes.customTypes_api.Vector3D[] decrementArray(customTypes.customTypes_api.Vector3D[] vec) { - Log.w(TAG, "request method decrementArray called, will call native"); + Log.i(TAG, "request method decrementArray called, will call native"); return nativeDecrementArray(vec); } diff --git a/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java b/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java index d02cc18..6d0c176 100644 --- a/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java +++ b/goldenmaster/counter/counterjniservice/CounterJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: CounterJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: CounterJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private CounterJniServiceFactory() @NonNull private static CounterJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); CounterJniServiceFactory t = new CounterJniServiceFactory(); t.start(); diff --git a/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java b/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java index 1fb1ef2..6eacc0c 100644 --- a/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java +++ b/goldenmaster/counter/counterjniservice/CounterJniServiceStarter.java @@ -21,11 +21,11 @@ public class CounterJniServiceStarter { public static ICounter start(Context context) { stop(context); androidService = new Intent(context, CounterServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); CounterJniServiceFactory factory = CounterJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for CounterJniServiceFactory"); + Log.i(TAG, "starter: factory set for CounterJniServiceFactory"); return CounterServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java b/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java index c39e34c..70c1ead 100644 --- a/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java +++ b/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java @@ -118,7 +118,7 @@ protected void onCreate(Bundle savedInstanceState) { bValueChanged.setText("valueChanged"); bValueChanged.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal valueChanged "); + Log.i(TAG, "broadcasting singal valueChanged "); customTypes.customTypes_api.Vector3D vector = new customTypes.customTypes_api.Vector3D(); org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); customTypes.customTypes_api.Vector3D[] vectorArray = new customTypes.customTypes_api.Vector3D(); @@ -186,7 +186,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, CounterServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = CounterServiceAdapter.setService(CounterServiceFactory.get()); mBackend.addEventListener(this); } @@ -212,45 +212,44 @@ protected void onDestroy() public void onVectorChanged(customTypes.customTypes_api.Vector3D newValue) { outputTextViewProp.setText("Property from service: vector " + newValue); - Log.w(TAG, "Property from service: vector " + newValue); + Log.i(TAG, "Property from service: vector " + newValue); } @Override public void onExternVectorChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D newValue) { outputTextViewProp.setText("Property from service: extern_vector " + newValue); - Log.w(TAG, "Property from service: extern_vector " + newValue); + Log.i(TAG, "Property from service: extern_vector " + newValue); } @Override public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] newValue) { outputTextViewProp.setText("Property from service: vectorArray " + newValue); - Log.w(TAG, "Property from service: vectorArray " + newValue); + Log.i(TAG, "Property from service: vectorArray " + newValue); } @Override public void onExternVectorArrayChanged(org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] newValue) { outputTextViewProp.setText("Property from service: extern_vectorArray " + newValue); - Log.w(TAG, "Property from service: extern_vectorArray " + newValue); + Log.i(TAG, "Property from service: extern_vectorArray " + newValue); } @Override public void onValueChanged(customTypes.customTypes_api.Vector3D vector, org.apache.commons.math3.geometry.euclidean.threed.Vector3D extern_vector, customTypes.customTypes_api.Vector3D[] vectorArray, org.apache.commons.math3.geometry.euclidean.threed.Vector3D[] extern_vectorArray) { String text = "Signal valueChanged "+ " " + vector+ " " + extern_vector+ " " + vectorArray+ " " + extern_vectorArray; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java index ab80de7..7d21021 100644 --- a/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java @@ -129,7 +129,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java index 46e0460..649c810 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: EnumInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: EnumInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private EnumInterfaceServiceFactory() @NonNull private static EnumInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); EnumInterfaceServiceFactory t = new EnumInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java index 86c51a2..cedb0c4 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class EnumInterfaceServiceStarter { public static IEnumInterface start(Context context) { stop(context); androidService = new Intent(context, EnumInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); EnumInterfaceServiceFactory factory = EnumInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for EnumInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for EnumInterfaceServiceFactory"); return EnumInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java index b48475a..fc571ef 100644 --- a/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java @@ -123,7 +123,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc0.setText("func0"); bFunc0.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func0 "); + Log.i(TAG, "CALLING METHOD func0 "); Enum0 param0 = Enum0.Value1; CompletableFuture method_res = mClient.func0Async(param0).thenApply( @@ -138,7 +138,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc1.setText("func1"); bFunc1.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func1 "); + Log.i(TAG, "CALLING METHOD func1 "); Enum1 param1 = Enum1.Value2; CompletableFuture method_res = mClient.func1Async(param1).thenApply( @@ -153,7 +153,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc2.setText("func2"); bFunc2.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func2 "); + Log.i(TAG, "CALLING METHOD func2 "); Enum2 param2 = Enum2.Value1; CompletableFuture method_res = mClient.func2Async(param2).thenApply( @@ -168,7 +168,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc3.setText("func3"); bFunc3.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func3 "); + Log.i(TAG, "CALLING METHOD func3 "); Enum3 param3 = Enum3.Value2; CompletableFuture method_res = mClient.func3Async(param3).thenApply( @@ -274,12 +274,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new EnumInterfaceClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -291,64 +291,64 @@ private void initServiceConnection( String servicePackage) public void onProp0Changed(Enum0 newValue) { outputTextViewProp.setText("Property from service: prop0 " + newValue); - Log.w(TAG, "Property from service: prop0 " + newValue); + Log.i(TAG, "Property from service: prop0 " + newValue); } @Override public void onProp1Changed(Enum1 newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onProp2Changed(Enum2 newValue) { outputTextViewProp.setText("Property from service: prop2 " + newValue); - Log.w(TAG, "Property from service: prop2 " + newValue); + Log.i(TAG, "Property from service: prop2 " + newValue); } @Override public void onProp3Changed(Enum3 newValue) { outputTextViewProp.setText("Property from service: prop3 " + newValue); - Log.w(TAG, "Property from service: prop3 " + newValue); + Log.i(TAG, "Property from service: prop3 " + newValue); } @Override public void onSig0(Enum0 param0) { String text = "Signal sig0 "+ " " + param0; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig1(Enum1 param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig2(Enum2 param2) { String text = "Signal sig2 "+ " " + param2; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig3(Enum3 param3) { String text = "Signal sig3 "+ " " + param3; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java b/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java index 776d4dc..ca2ebe8 100644 --- a/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java +++ b/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java @@ -121,7 +121,7 @@ public Enum3 getProp3() @Override public Enum0 func0(Enum0 param0) { - Log.w(TAG, "request method func0 called, returnig default"); + Log.i(TAG, "request method func0 called, returnig default"); return Enum0.Value0; } @@ -134,7 +134,7 @@ public CompletableFuture func0Async(Enum0 param0) { @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return Enum1.Value1; } @@ -147,7 +147,7 @@ public CompletableFuture func1Async(Enum1 param1) { @Override public Enum2 func2(Enum2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return Enum2.Value2; } @@ -160,7 +160,7 @@ public CompletableFuture func2Async(Enum2 param2) { @Override public Enum3 func3(Enum3 param3) { - Log.w(TAG, "request method func3 called, returnig default"); + Log.i(TAG, "request method func3 called, returnig default"); return Enum3.Value3; } diff --git a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java index 02e66d7..6583e00 100644 --- a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java +++ b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java @@ -180,7 +180,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new EnumInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -194,7 +194,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -202,49 +202,49 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp0Changed(Enum0 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp0Changed(newValue); } @Override public void onProp1Changed(Enum1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(Enum2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onProp3Changed(Enum3 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp3Changed(newValue); } @Override public void onSig0(Enum0 param0) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig0 "+ " " + param0); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig0 "+ " " + param0); nativeOnSig0(param0); } @Override public void onSig1(Enum1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(Enum2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param2); nativeOnSig2(param2); } @Override public void onSig3(Enum3 param3) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param3); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param3); nativeOnSig3(param3); } private native void nativeOnProp0Changed(Enum0 prop0); diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java index d6aefc0..e9bfa39 100644 --- a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java @@ -100,7 +100,7 @@ public Enum3 getProp3() @Override public Enum0 func0(Enum0 param0) { - Log.w(TAG, "request method func0 called, will call native"); + Log.i(TAG, "request method func0 called, will call native"); return nativeFunc0(param0); } @@ -113,7 +113,7 @@ public CompletableFuture func0Async(Enum0 param0) { @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -126,7 +126,7 @@ public CompletableFuture func1Async(Enum1 param1) { @Override public Enum2 func2(Enum2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param2); } @@ -139,7 +139,7 @@ public CompletableFuture func2Async(Enum2 param2) { @Override public Enum3 func3(Enum3 param3) { - Log.w(TAG, "request method func3 called, will call native"); + Log.i(TAG, "request method func3 called, will call native"); return nativeFunc3(param3); } diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java index 9f74071..38b1d3d 100644 --- a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: EnumInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: EnumInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private EnumInterfaceJniServiceFactory() @NonNull private static EnumInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); EnumInterfaceJniServiceFactory t = new EnumInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java index 03d2096..790193e 100644 --- a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class EnumInterfaceJniServiceStarter { public static IEnumInterface start(Context context) { stop(context); androidService = new Intent(context, EnumInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); EnumInterfaceJniServiceFactory factory = EnumInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for EnumInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for EnumInterfaceJniServiceFactory"); return EnumInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java index b7e733d..49c4166 100644 --- a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java @@ -126,7 +126,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig0.setText("sig0"); bSig0.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig0 "); + Log.i(TAG, "broadcasting singal sig0 "); Enum0 param0 = Enum0.Value1; mBackend.fireSig0(param0); }); @@ -136,7 +136,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig1.setText("sig1"); bSig1.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig1 "); + Log.i(TAG, "broadcasting singal sig1 "); Enum1 param1 = Enum1.Value2; mBackend.fireSig1(param1); }); @@ -146,7 +146,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig2.setText("sig2"); bSig2.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig2 "); + Log.i(TAG, "broadcasting singal sig2 "); Enum2 param2 = Enum2.Value1; mBackend.fireSig2(param2); }); @@ -156,7 +156,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig3.setText("sig3"); bSig3.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig3 "); + Log.i(TAG, "broadcasting singal sig3 "); Enum3 param3 = Enum3.Value2; mBackend.fireSig3(param3); }); @@ -221,7 +221,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, EnumInterfaceServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = EnumInterfaceServiceAdapter.setService(EnumInterfaceServiceFactory.get()); mBackend.addEventListener(this); } @@ -247,66 +247,65 @@ protected void onDestroy() public void onProp0Changed(Enum0 newValue) { outputTextViewProp.setText("Property from service: prop0 " + newValue); - Log.w(TAG, "Property from service: prop0 " + newValue); + Log.i(TAG, "Property from service: prop0 " + newValue); } @Override public void onProp1Changed(Enum1 newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onProp2Changed(Enum2 newValue) { outputTextViewProp.setText("Property from service: prop2 " + newValue); - Log.w(TAG, "Property from service: prop2 " + newValue); + Log.i(TAG, "Property from service: prop2 " + newValue); } @Override public void onProp3Changed(Enum3 newValue) { outputTextViewProp.setText("Property from service: prop3 " + newValue); - Log.w(TAG, "Property from service: prop3 " + newValue); + Log.i(TAG, "Property from service: prop3 " + newValue); } @Override public void onSig0(Enum0 param0) { String text = "Signal sig0 "+ " " + param0; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig1(Enum1 param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig2(Enum2 param2) { String text = "Signal sig2 "+ " " + param2; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig3(Enum3 param3) { String text = "Signal sig3 "+ " " + param3; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java index a86148d..08f022a 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/main/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClient.java @@ -117,7 +117,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java index 48a95e1..454fe4b 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: EmptyIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: EmptyIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private EmptyIfServiceFactory() @NonNull private static EmptyIfServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); EmptyIfServiceFactory t = new EmptyIfServiceFactory(); t.start(); diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java index ecc6062..acf3a7e 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceStarter.java @@ -21,11 +21,11 @@ public class EmptyIfServiceStarter { public static IEmptyIf start(Context context) { stop(context); androidService = new Intent(context, EmptyIfServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); EmptyIfServiceFactory factory = EmptyIfServiceFactory.get(); - Log.w(TAG, "starter: factory set for EmptyIfServiceFactory"); + Log.i(TAG, "starter: factory set for EmptyIfServiceFactory"); return EmptyIfServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java index e76f86c..29dfc5a 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/java/tbIfaceimport/tbIfaceimport_client_example/TbIfaceimportTestClientApp.java @@ -158,12 +158,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new EmptyIfClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -176,11 +176,11 @@ public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java index a22bebf..5df307e 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java @@ -48,7 +48,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new EmptyIfClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -62,7 +62,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java index 57a0e7b..474888c 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: EmptyIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: EmptyIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private EmptyIfJniServiceFactory() @NonNull private static EmptyIfJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); EmptyIfJniServiceFactory t = new EmptyIfJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java index 0589a27..30ac033 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniServiceStarter.java @@ -21,11 +21,11 @@ public class EmptyIfJniServiceStarter { public static IEmptyIf start(Context context) { stop(context); androidService = new Intent(context, EmptyIfServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); EmptyIfJniServiceFactory factory = EmptyIfJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for EmptyIfJniServiceFactory"); + Log.i(TAG, "starter: factory set for EmptyIfJniServiceFactory"); return EmptyIfServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java index d794121..226e68b 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java @@ -129,7 +129,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, EmptyIfServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = EmptyIfServiceAdapter.setService(EmptyIfServiceFactory.get()); mBackend.addEventListener(this); } @@ -156,13 +156,12 @@ public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java index 9e38fc1..9b8566c 100644 --- a/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java +++ b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java index 4109426..2f99ed3 100644 --- a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NamEsServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NamEsServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NamEsServiceFactory() @NonNull private static NamEsServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NamEsServiceFactory t = new NamEsServiceFactory(); t.start(); diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java index 3c7d29e..d09d0fb 100644 --- a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceStarter.java @@ -21,11 +21,11 @@ public class NamEsServiceStarter { public static INamEs start(Context context) { stop(context); androidService = new Intent(context, NamEsServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NamEsServiceFactory factory = NamEsServiceFactory.get(); - Log.w(TAG, "starter: factory set for NamEsServiceFactory"); + Log.i(TAG, "starter: factory set for NamEsServiceFactory"); return NamEsServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java index 803acf5..06c61a3 100644 --- a/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java @@ -117,7 +117,7 @@ protected void onCreate(Bundle savedInstanceState) { bSomeFunction.setText("SOME_FUNCTION"); bSomeFunction.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD SOME_FUNCTION "); + Log.i(TAG, "CALLING METHOD SOME_FUNCTION "); boolean SOME_PARAM = true; CompletableFuture method_res = mClient.someFunctionAsync(SOME_PARAM).thenApply( @@ -132,7 +132,7 @@ protected void onCreate(Bundle savedInstanceState) { bSomeFunction2.setText("Some_Function2"); bSomeFunction2.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD Some_Function2 "); + Log.i(TAG, "CALLING METHOD Some_Function2 "); boolean Some_Param = true; CompletableFuture method_res = mClient.someFunction2Async(Some_Param).thenApply( @@ -238,12 +238,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new NamEsClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -255,50 +255,50 @@ private void initServiceConnection( String servicePackage) public void onSwitchChanged(boolean newValue) { outputTextViewProp.setText("Property from service: Switch " + newValue); - Log.w(TAG, "Property from service: Switch " + newValue); + Log.i(TAG, "Property from service: Switch " + newValue); } @Override public void onSomePropertyChanged(int newValue) { outputTextViewProp.setText("Property from service: SOME_PROPERTY " + newValue); - Log.w(TAG, "Property from service: SOME_PROPERTY " + newValue); + Log.i(TAG, "Property from service: SOME_PROPERTY " + newValue); } @Override public void onSomePoperty2Changed(int newValue) { outputTextViewProp.setText("Property from service: Some_Poperty2 " + newValue); - Log.w(TAG, "Property from service: Some_Poperty2 " + newValue); + Log.i(TAG, "Property from service: Some_Poperty2 " + newValue); } @Override public void onEnumPropertyChanged(EnumWithUnderScores newValue) { outputTextViewProp.setText("Property from service: enum_property " + newValue); - Log.w(TAG, "Property from service: enum_property " + newValue); + Log.i(TAG, "Property from service: enum_property " + newValue); } @Override public void onSomeSignal(boolean SOME_PARAM) { String text = "Signal SOME_SIGNAL "+ " " + SOME_PARAM; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSomeSignal2(boolean Some_Param) { String text = "Signal Some_Signal2 "+ " " + Some_Param; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java index ec5a521..77eaf18 100644 --- a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java +++ b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java @@ -118,7 +118,7 @@ public EnumWithUnderScores getEnumProperty() @Override public void someFunction(boolean SOME_PARAM) { - Log.w(TAG, "request method someFunction called, returnig default"); + Log.i(TAG, "request method someFunction called, returnig default"); return ; } @@ -131,7 +131,7 @@ public CompletableFuture someFunctionAsync(boolean SOME_PARAM) { @Override public void someFunction2(boolean Some_Param) { - Log.w(TAG, "request method someFunction2 called, returnig default"); + Log.i(TAG, "request method someFunction2 called, returnig default"); return ; } diff --git a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java index c15f2a2..7c6cdc9 100644 --- a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java +++ b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java @@ -138,7 +138,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NamEsClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -152,7 +152,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -160,37 +160,37 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onSwitchChanged(boolean newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnSwitchChanged(newValue); } @Override public void onSomePropertyChanged(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnSomePropertyChanged(newValue); } @Override public void onSomePoperty2Changed(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnSomePoperty2Changed(newValue); } @Override public void onEnumPropertyChanged(EnumWithUnderScores newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnEnumPropertyChanged(newValue); } @Override public void onSomeSignal(boolean SOME_PARAM) { - Log.w(TAG, "NOTIFICATION from messenger client Signal SOME_SIGNAL "+ " " + SOME_PARAM); + Log.i(TAG, "NOTIFICATION from messenger client Signal SOME_SIGNAL "+ " " + SOME_PARAM); nativeOnSomeSignal(SOME_PARAM); } @Override public void onSomeSignal2(boolean Some_Param) { - Log.w(TAG, "NOTIFICATION from messenger client Signal Some_Signal2 "+ " " + Some_Param); + Log.i(TAG, "NOTIFICATION from messenger client Signal Some_Signal2 "+ " " + Some_Param); nativeOnSomeSignal2(Some_Param); } private native void nativeOnSwitchChanged(boolean Switch); diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java index dc5c8d5..168f990 100644 --- a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java @@ -94,7 +94,7 @@ public EnumWithUnderScores getEnumProperty() @Override public void someFunction(boolean SOME_PARAM) { - Log.w(TAG, "request method someFunction called, will call native"); + Log.i(TAG, "request method someFunction called, will call native"); nativeSomeFunction(SOME_PARAM); } @@ -107,7 +107,7 @@ public CompletableFuture someFunctionAsync(boolean SOME_PARAM) { @Override public void someFunction2(boolean Some_Param) { - Log.w(TAG, "request method someFunction2 called, will call native"); + Log.i(TAG, "request method someFunction2 called, will call native"); nativeSomeFunction2(Some_Param); } diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java index 7e6b85e..ec48a66 100644 --- a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NamEsJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NamEsJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NamEsJniServiceFactory() @NonNull private static NamEsJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NamEsJniServiceFactory t = new NamEsJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java index 2c89ef9..5491b98 100644 --- a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniServiceStarter.java @@ -21,11 +21,11 @@ public class NamEsJniServiceStarter { public static INamEs start(Context context) { stop(context); androidService = new Intent(context, NamEsServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NamEsJniServiceFactory factory = NamEsJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NamEsJniServiceFactory"); + Log.i(TAG, "starter: factory set for NamEsJniServiceFactory"); return NamEsServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java index 3ba3026..d8771b6 100644 --- a/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java @@ -120,7 +120,7 @@ protected void onCreate(Bundle savedInstanceState) { bSomeSignal.setText("SOME_SIGNAL"); bSomeSignal.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal SOME_SIGNAL "); + Log.i(TAG, "broadcasting singal SOME_SIGNAL "); boolean SOME_PARAM = true; mBackend.fireSomeSignal(SOME_PARAM); }); @@ -130,7 +130,7 @@ protected void onCreate(Bundle savedInstanceState) { bSomeSignal2.setText("Some_Signal2"); bSomeSignal2.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal Some_Signal2 "); + Log.i(TAG, "broadcasting singal Some_Signal2 "); boolean Some_Param = true; mBackend.fireSomeSignal2(Some_Param); }); @@ -195,7 +195,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, NamEsServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = NamEsServiceAdapter.setService(NamEsServiceFactory.get()); mBackend.addEventListener(this); } @@ -221,52 +221,51 @@ protected void onDestroy() public void onSwitchChanged(boolean newValue) { outputTextViewProp.setText("Property from service: Switch " + newValue); - Log.w(TAG, "Property from service: Switch " + newValue); + Log.i(TAG, "Property from service: Switch " + newValue); } @Override public void onSomePropertyChanged(int newValue) { outputTextViewProp.setText("Property from service: SOME_PROPERTY " + newValue); - Log.w(TAG, "Property from service: SOME_PROPERTY " + newValue); + Log.i(TAG, "Property from service: SOME_PROPERTY " + newValue); } @Override public void onSomePoperty2Changed(int newValue) { outputTextViewProp.setText("Property from service: Some_Poperty2 " + newValue); - Log.w(TAG, "Property from service: Some_Poperty2 " + newValue); + Log.i(TAG, "Property from service: Some_Poperty2 " + newValue); } @Override public void onEnumPropertyChanged(EnumWithUnderScores newValue) { outputTextViewProp.setText("Property from service: enum_property " + newValue); - Log.w(TAG, "Property from service: enum_property " + newValue); + Log.i(TAG, "Property from service: enum_property " + newValue); } @Override public void onSomeSignal(boolean SOME_PARAM) { String text = "Signal SOME_SIGNAL "+ " " + SOME_PARAM; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSomeSignal2(boolean Some_Param) { String text = "Signal Some_Signal2 "+ " " + Some_Param; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java index 4ef36cc..a7e367d 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java index 55a9270..d4ac017 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java @@ -118,7 +118,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java index c9576c5..ca80362 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: ParentIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: ParentIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private ParentIfServiceFactory() @NonNull private static ParentIfServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); ParentIfServiceFactory t = new ParentIfServiceFactory(); t.start(); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java index aa11b0f..ba6486f 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceStarter.java @@ -21,11 +21,11 @@ public class ParentIfServiceStarter { public static IParentIf start(Context context) { stop(context); androidService = new Intent(context, ParentIfServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); ParentIfServiceFactory factory = ParentIfServiceFactory.get(); - Log.w(TAG, "starter: factory set for ParentIfServiceFactory"); + Log.i(TAG, "starter: factory set for ParentIfServiceFactory"); return ParentIfServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java index 792436c..f390003 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SimpleLocalIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SimpleLocalIfServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SimpleLocalIfServiceFactory() @NonNull private static SimpleLocalIfServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SimpleLocalIfServiceFactory t = new SimpleLocalIfServiceFactory(); t.start(); diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java index 2106d16..fe65ba3 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceStarter.java @@ -21,11 +21,11 @@ public class SimpleLocalIfServiceStarter { public static ISimpleLocalIf start(Context context) { stop(context); androidService = new Intent(context, SimpleLocalIfServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SimpleLocalIfServiceFactory factory = SimpleLocalIfServiceFactory.get(); - Log.w(TAG, "starter: factory set for SimpleLocalIfServiceFactory"); + Log.i(TAG, "starter: factory set for SimpleLocalIfServiceFactory"); return SimpleLocalIfServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java index 29f6045..135ae01 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/java/tbRefIfaces/tbRefIfaces_client_example/TbRefIfacesTestClientApp.java @@ -79,7 +79,7 @@ protected void onCreate(Bundle savedInstanceState) { bIntMethod.setText("intMethod"); bIntMethod.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD intMethod "); + Log.i(TAG, "CALLING METHOD intMethod "); int param = 1; CompletableFuture method_res = mClient.intMethodAsync(param).thenApply( @@ -185,12 +185,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new SimpleLocalIfClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -202,25 +202,25 @@ private void initServiceConnection( String servicePackage) public void onIntPropertyChanged(int newValue) { outputTextViewProp.setText("Property from service: intProperty " + newValue); - Log.w(TAG, "Property from service: intProperty " + newValue); + Log.i(TAG, "Property from service: intProperty " + newValue); } @Override public void onIntSignal(int param) { String text = "Signal intSignal "+ " " + param; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java index b67b9df..8a08792 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java @@ -119,7 +119,7 @@ public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList() @Override public ISimpleLocalIf localIfMethod(ISimpleLocalIf param) { - Log.w(TAG, "request method localIfMethod called, returnig default"); + Log.i(TAG, "request method localIfMethod called, returnig default"); return null; } @@ -132,7 +132,7 @@ public CompletableFuture localIfMethodAsync(ISimpleLocalIf para @Override public ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param) { - Log.w(TAG, "request method localIfMethodList called, returnig default"); + Log.i(TAG, "request method localIfMethodList called, returnig default"); return new ISimpleLocalIf[]{}; } @@ -145,7 +145,7 @@ public CompletableFuture localIfMethodListAsync(ISimpleLocalI @Override public tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { - Log.w(TAG, "request method importedIfMethod called, returnig default"); + Log.i(TAG, "request method importedIfMethod called, returnig default"); return null; } @@ -158,7 +158,7 @@ public CompletableFuture importedIfMe @Override public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { - Log.w(TAG, "request method importedIfMethodList called, returnig default"); + Log.i(TAG, "request method importedIfMethodList called, returnig default"); return new tbIfaceimport.tbIfaceimport_api.IEmptyIf[]{}; } diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java index 42252e1..f24b04f 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java @@ -54,7 +54,7 @@ public int getIntProperty() @Override public int intMethod(int param) { - Log.w(TAG, "request method intMethod called, returnig default"); + Log.i(TAG, "request method intMethod called, returnig default"); return 0; } diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java index 9e5da76..98f17e2 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java @@ -174,7 +174,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new ParentIfClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -188,7 +188,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -196,49 +196,49 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onLocalIfChanged(ISimpleLocalIf newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnLocalIfChanged(newValue); } @Override public void onLocalIfListChanged(ISimpleLocalIf[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnLocalIfListChanged(newValue); } @Override public void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnImportedIfChanged(newValue); } @Override public void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnImportedIfListChanged(newValue); } @Override public void onLocalIfSignal(ISimpleLocalIf param) { - Log.w(TAG, "NOTIFICATION from messenger client Signal localIfSignal "+ " " + param); + Log.i(TAG, "NOTIFICATION from messenger client Signal localIfSignal "+ " " + param); nativeOnLocalIfSignal(param); } @Override public void onLocalIfSignalList(ISimpleLocalIf[] param) { - Log.w(TAG, "NOTIFICATION from messenger client Signal localIfSignalList "+ " " + param); + Log.i(TAG, "NOTIFICATION from messenger client Signal localIfSignalList "+ " " + param); nativeOnLocalIfSignalList(param); } @Override public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { - Log.w(TAG, "NOTIFICATION from messenger client Signal importedIfSignal "+ " " + param); + Log.i(TAG, "NOTIFICATION from messenger client Signal importedIfSignal "+ " " + param); nativeOnImportedIfSignal(param); } @Override public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { - Log.w(TAG, "NOTIFICATION from messenger client Signal importedIfSignalList "+ " " + param); + Log.i(TAG, "NOTIFICATION from messenger client Signal importedIfSignalList "+ " " + param); nativeOnImportedIfSignalList(param); } private native void nativeOnLocalIfChanged(ISimpleLocalIf localIf); diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java index c33366a..5aa30f8 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java @@ -79,7 +79,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SimpleLocalIfClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -93,7 +93,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -101,13 +101,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onIntPropertyChanged(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnIntPropertyChanged(newValue); } @Override public void onIntSignal(int param) { - Log.w(TAG, "NOTIFICATION from messenger client Signal intSignal "+ " " + param); + Log.i(TAG, "NOTIFICATION from messenger client Signal intSignal "+ " " + param); nativeOnIntSignal(param); } private native void nativeOnIntPropertyChanged(int intProperty); diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java index b2b2279..832c5af 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java @@ -94,7 +94,7 @@ public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] getImportedIfList() @Override public ISimpleLocalIf localIfMethod(ISimpleLocalIf param) { - Log.w(TAG, "request method localIfMethod called, will call native"); + Log.i(TAG, "request method localIfMethod called, will call native"); return nativeLocalIfMethod(param); } @@ -107,7 +107,7 @@ public CompletableFuture localIfMethodAsync(ISimpleLocalIf para @Override public ISimpleLocalIf[] localIfMethodList(ISimpleLocalIf[] param) { - Log.w(TAG, "request method localIfMethodList called, will call native"); + Log.i(TAG, "request method localIfMethodList called, will call native"); return nativeLocalIfMethodList(param); } @@ -120,7 +120,7 @@ public CompletableFuture localIfMethodListAsync(ISimpleLocalI @Override public tbIfaceimport.tbIfaceimport_api.IEmptyIf importedIfMethod(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) { - Log.w(TAG, "request method importedIfMethod called, will call native"); + Log.i(TAG, "request method importedIfMethod called, will call native"); return nativeImportedIfMethod(param); } @@ -133,7 +133,7 @@ public CompletableFuture importedIfMe @Override public tbIfaceimport.tbIfaceimport_api.IEmptyIf[] importedIfMethodList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) { - Log.w(TAG, "request method importedIfMethodList called, will call native"); + Log.i(TAG, "request method importedIfMethodList called, will call native"); return nativeImportedIfMethodList(param); } diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java index 92534bf..69fd165 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: ParentIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: ParentIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private ParentIfJniServiceFactory() @NonNull private static ParentIfJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); ParentIfJniServiceFactory t = new ParentIfJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java index 086745c..db12ea1 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniServiceStarter.java @@ -21,11 +21,11 @@ public class ParentIfJniServiceStarter { public static IParentIf start(Context context) { stop(context); androidService = new Intent(context, ParentIfServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); ParentIfJniServiceFactory factory = ParentIfJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for ParentIfJniServiceFactory"); + Log.i(TAG, "starter: factory set for ParentIfJniServiceFactory"); return ParentIfServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java index 82a4a0c..0df6552 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java @@ -47,7 +47,7 @@ public int getIntProperty() @Override public int intMethod(int param) { - Log.w(TAG, "request method intMethod called, will call native"); + Log.i(TAG, "request method intMethod called, will call native"); return nativeIntMethod(param); } diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java index 2813b8d..b3972ee 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SimpleLocalIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SimpleLocalIfJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SimpleLocalIfJniServiceFactory() @NonNull private static SimpleLocalIfJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SimpleLocalIfJniServiceFactory t = new SimpleLocalIfJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java index f38c23a..d2307ab 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniServiceStarter.java @@ -21,11 +21,11 @@ public class SimpleLocalIfJniServiceStarter { public static ISimpleLocalIf start(Context context) { stop(context); androidService = new Intent(context, SimpleLocalIfServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SimpleLocalIfJniServiceFactory factory = SimpleLocalIfJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SimpleLocalIfJniServiceFactory"); + Log.i(TAG, "starter: factory set for SimpleLocalIfJniServiceFactory"); return SimpleLocalIfServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java index e235230..c82fecc 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java @@ -85,7 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { bIntSignal.setText("intSignal"); bIntSignal.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal intSignal "); + Log.i(TAG, "broadcasting singal intSignal "); int param = 1; mBackend.fireIntSignal(param); }); @@ -150,7 +150,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, SimpleLocalIfServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = SimpleLocalIfServiceAdapter.setService(SimpleLocalIfServiceFactory.get()); mBackend.addEventListener(this); } @@ -176,27 +176,26 @@ protected void onDestroy() public void onIntPropertyChanged(int newValue) { outputTextViewProp.setText("Property from service: intProperty " + newValue); - Log.w(TAG, "Property from service: intProperty " + newValue); + Log.i(TAG, "Property from service: intProperty " + newValue); } @Override public void onIntSignal(int param) { String text = "Signal intSignal "+ " " + param; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java index b2b1f7f..a124fc3 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java @@ -120,7 +120,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java index 10ac29d..a48e947 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java index 18b0363..3199708 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java @@ -120,7 +120,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java index ef67fc3..986ad54 100644 --- a/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java index 2351c69..6710322 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameEnum1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum1InterfaceServiceFactory() @NonNull private static SameEnum1InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum1InterfaceServiceFactory t = new SameEnum1InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java index 25ad342..187c556 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum1InterfaceServiceStarter { public static ISameEnum1Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum1InterfaceServiceFactory factory = SameEnum1InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); return SameEnum1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java index 0d51f92..4ab1136 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameEnum2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum2InterfaceServiceFactory() @NonNull private static SameEnum2InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum2InterfaceServiceFactory t = new SameEnum2InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java index 3dabc4d..c29ea81 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum2InterfaceServiceStarter { public static ISameEnum2Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum2InterfaceServiceFactory factory = SameEnum2InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); return SameEnum2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java index 24fe613..e781bb2 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct1InterfaceServiceFactory() @NonNull private static SameStruct1InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct1InterfaceServiceFactory t = new SameStruct1InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java index ddd614c..3aeee7b 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct1InterfaceServiceStarter { public static ISameStruct1Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct1InterfaceServiceFactory factory = SameStruct1InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); return SameStruct1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java index c184793..c22be3a 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct2InterfaceServiceFactory() @NonNull private static SameStruct2InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct2InterfaceServiceFactory t = new SameStruct2InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java index f0dae12..78484ee 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct2InterfaceServiceStarter { public static ISameStruct2Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct2InterfaceServiceFactory factory = SameStruct2InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); return SameStruct2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java index ddfbf8a..a1d370a 100644 --- a/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java @@ -80,7 +80,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc1.setText("func1"); bFunc1.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func1 "); + Log.i(TAG, "CALLING METHOD func1 "); Struct1 param1 = new Struct1(); CompletableFuture method_res = mClient.func1Async(param1).thenApply( @@ -186,12 +186,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new SameStruct1InterfaceClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -203,25 +203,25 @@ private void initServiceConnection( String servicePackage) public void onProp1Changed(Struct1 newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onSig1(Struct1 param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java index 6246973..19a05ec 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java @@ -55,7 +55,7 @@ public Enum1 getProp1() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return Enum1.Value1; } diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java index e2333e7..29eefe4 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java @@ -77,7 +77,7 @@ public Enum2 getProp2() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return Enum1.Value1; } @@ -90,7 +90,7 @@ public CompletableFuture func1Async(Enum1 param1) { @Override public Enum1 func2(Enum1 param1, Enum2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return Enum1.Value1; } diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java index 1c2426f..5c1a262 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java @@ -56,7 +56,7 @@ public Struct1 getProp1() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new Struct1(); } diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java index 6aeb87d..413cb27 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java @@ -79,7 +79,7 @@ public Struct2 getProp2() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new Struct1(); } @@ -92,7 +92,7 @@ public CompletableFuture func1Async(Struct1 param1) { @Override public Struct1 func2(Struct1 param1, Struct2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return new Struct1(); } diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java index 1bc8634..adcedf9 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java @@ -81,7 +81,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameEnum1InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -95,7 +95,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -103,13 +103,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Enum1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onSig1(Enum1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } private native void nativeOnProp1Changed(Enum1 prop1); diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java index 2c1c270..58da0dc 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java @@ -114,7 +114,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameEnum2InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -128,7 +128,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -136,25 +136,25 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Enum1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(Enum2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onSig1(Enum1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(Enum1 param1, Enum2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } private native void nativeOnProp1Changed(Enum1 prop1); diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java index 1d927af..78879e9 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java @@ -81,7 +81,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameStruct1InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -95,7 +95,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -103,13 +103,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Struct1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onSig1(Struct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } private native void nativeOnProp1Changed(Struct1 prop1); diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java index bbebab3..1502cf5 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java @@ -114,7 +114,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameStruct2InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -128,7 +128,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -136,25 +136,25 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Struct2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(Struct2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onSig1(Struct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(Struct1 param1, Struct2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } private native void nativeOnProp1Changed(Struct2 prop1); diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java index 508d226..009de98 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java @@ -49,7 +49,7 @@ public Enum1 getProp1() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java index e15624b..482c5b7 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameEnum1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum1InterfaceJniServiceFactory() @NonNull private static SameEnum1InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum1InterfaceJniServiceFactory t = new SameEnum1InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java index e199613..051dbfb 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum1InterfaceJniServiceStarter { public static ISameEnum1Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum1InterfaceJniServiceFactory factory = SameEnum1InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); return SameEnum1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java index f736fc0..ef7cafc 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java @@ -66,7 +66,7 @@ public Enum2 getProp2() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -79,7 +79,7 @@ public CompletableFuture func1Async(Enum1 param1) { @Override public Enum1 func2(Enum1 param1, Enum2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java index 0624d94..c0cb591 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameEnum2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum2InterfaceJniServiceFactory() @NonNull private static SameEnum2InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum2InterfaceJniServiceFactory t = new SameEnum2InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java index c7949e0..e3fff0d 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum2InterfaceJniServiceStarter { public static ISameEnum2Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum2InterfaceJniServiceFactory factory = SameEnum2InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); return SameEnum2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java index d4186b7..ffecb70 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java @@ -49,7 +49,7 @@ public Struct1 getProp1() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java index 9d61054..f0ba3f9 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct1InterfaceJniServiceFactory() @NonNull private static SameStruct1InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct1InterfaceJniServiceFactory t = new SameStruct1InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java index 50ac6ec..4413ac1 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct1InterfaceJniServiceStarter { public static ISameStruct1Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct1InterfaceJniServiceFactory factory = SameStruct1InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); return SameStruct1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java index 845f59c..74db547 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java @@ -66,7 +66,7 @@ public Struct2 getProp2() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -79,7 +79,7 @@ public CompletableFuture func1Async(Struct1 param1) { @Override public Struct1 func2(Struct1 param1, Struct2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java index 8f8215a..178077b 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct2InterfaceJniServiceFactory() @NonNull private static SameStruct2InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct2InterfaceJniServiceFactory t = new SameStruct2InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java index f1c2f2c..53cd704 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct2InterfaceJniServiceStarter { public static ISameStruct2Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct2InterfaceJniServiceFactory factory = SameStruct2InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); return SameStruct2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java index 356ac01..90e30ad 100644 --- a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java @@ -87,7 +87,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig1.setText("sig1"); bSig1.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig1 "); + Log.i(TAG, "broadcasting singal sig1 "); Struct1 param1 = new Struct1(); mBackend.fireSig1(param1); }); @@ -152,7 +152,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, SameStruct1InterfaceServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = SameStruct1InterfaceServiceAdapter.setService(SameStruct1InterfaceServiceFactory.get()); mBackend.addEventListener(this); } @@ -178,27 +178,26 @@ protected void onDestroy() public void onProp1Changed(Struct1 newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onSig1(Struct1 param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java index 4cda64a..8bdef54 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java @@ -120,7 +120,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java index 986040e..68d6b03 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java index 255970e..144dadb 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java @@ -120,7 +120,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java index de2d102..5ba0b46 100644 --- a/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java index 70e89ed..ce76741 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameEnum1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum1InterfaceServiceFactory() @NonNull private static SameEnum1InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum1InterfaceServiceFactory t = new SameEnum1InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java index a1a1f96..2e5c6a6 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum1InterfaceServiceStarter { public static ISameEnum1Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum1InterfaceServiceFactory factory = SameEnum1InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); return SameEnum1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java index 4cfbe3e..b2c064f 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameEnum2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum2InterfaceServiceFactory() @NonNull private static SameEnum2InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum2InterfaceServiceFactory t = new SameEnum2InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java index a07aefe..b1e8ab5 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum2InterfaceServiceStarter { public static ISameEnum2Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum2InterfaceServiceFactory factory = SameEnum2InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); return SameEnum2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java index 0e1b16b..3cd3076 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct1InterfaceServiceFactory() @NonNull private static SameStruct1InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct1InterfaceServiceFactory t = new SameStruct1InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java index 3f277cc..8239274 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct1InterfaceServiceStarter { public static ISameStruct1Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct1InterfaceServiceFactory factory = SameStruct1InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); return SameStruct1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java index fd015cb..4d39bd9 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SameStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct2InterfaceServiceFactory() @NonNull private static SameStruct2InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct2InterfaceServiceFactory t = new SameStruct2InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java index 63df222..a78b49a 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct2InterfaceServiceStarter { public static ISameStruct2Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct2InterfaceServiceFactory factory = SameStruct2InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); return SameStruct2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java index 2b54b5c..5d0f2bb 100644 --- a/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java @@ -80,7 +80,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc1.setText("func1"); bFunc1.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func1 "); + Log.i(TAG, "CALLING METHOD func1 "); Struct1 param1 = new Struct1(); CompletableFuture method_res = mClient.func1Async(param1).thenApply( @@ -186,12 +186,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new SameStruct1InterfaceClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -203,25 +203,25 @@ private void initServiceConnection( String servicePackage) public void onProp1Changed(Struct1 newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onSig1(Struct1 param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java index 4021bc2..f01029e 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java @@ -55,7 +55,7 @@ public Enum1 getProp1() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return Enum1.Value1; } diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java index f263275..edbbb31 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java @@ -77,7 +77,7 @@ public Enum2 getProp2() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return Enum1.Value1; } @@ -90,7 +90,7 @@ public CompletableFuture func1Async(Enum1 param1) { @Override public Enum1 func2(Enum1 param1, Enum2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return Enum1.Value1; } diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java index bf2d301..c99dacc 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java @@ -56,7 +56,7 @@ public Struct1 getProp1() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new Struct1(); } diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java index 8260d2d..d07040e 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java @@ -79,7 +79,7 @@ public Struct2 getProp2() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new Struct1(); } @@ -92,7 +92,7 @@ public CompletableFuture func1Async(Struct1 param1) { @Override public Struct1 func2(Struct1 param1, Struct2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return new Struct1(); } diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java index 7378bfd..e00cc4e 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java @@ -81,7 +81,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameEnum1InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -95,7 +95,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -103,13 +103,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Enum1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onSig1(Enum1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } private native void nativeOnProp1Changed(Enum1 prop1); diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java index b171439..da46e9f 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java @@ -114,7 +114,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameEnum2InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -128,7 +128,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -136,25 +136,25 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Enum1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(Enum2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onSig1(Enum1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(Enum1 param1, Enum2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } private native void nativeOnProp1Changed(Enum1 prop1); diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java index 10aa834..ab90613 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java @@ -81,7 +81,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameStruct1InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -95,7 +95,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -103,13 +103,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Struct1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onSig1(Struct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } private native void nativeOnProp1Changed(Struct1 prop1); diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java index 9365e4c..89efdfe 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java @@ -114,7 +114,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SameStruct2InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -128,7 +128,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -136,25 +136,25 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(Struct2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(Struct2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onSig1(Struct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(Struct1 param1, Struct2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } private native void nativeOnProp1Changed(Struct2 prop1); diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java index 3aa2b43..7de9556 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java @@ -49,7 +49,7 @@ public Enum1 getProp1() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java index c394d20..272d0ef 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameEnum1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum1InterfaceJniServiceFactory() @NonNull private static SameEnum1InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum1InterfaceJniServiceFactory t = new SameEnum1InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java index 3f78ed4..9c35650 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum1InterfaceJniServiceStarter { public static ISameEnum1Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum1InterfaceJniServiceFactory factory = SameEnum1InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); return SameEnum1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java index 0901473..f3d5c4d 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java @@ -66,7 +66,7 @@ public Enum2 getProp2() @Override public Enum1 func1(Enum1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -79,7 +79,7 @@ public CompletableFuture func1Async(Enum1 param1) { @Override public Enum1 func2(Enum1 param1, Enum2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java index f056ba1..a5c7cbf 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameEnum2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameEnum2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameEnum2InterfaceJniServiceFactory() @NonNull private static SameEnum2InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameEnum2InterfaceJniServiceFactory t = new SameEnum2InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java index 3d13945..1d26dd7 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameEnum2InterfaceJniServiceStarter { public static ISameEnum2Interface start(Context context) { stop(context); androidService = new Intent(context, SameEnum2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameEnum2InterfaceJniServiceFactory factory = SameEnum2InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); return SameEnum2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java index cfcf6b4..fa1417f 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java @@ -49,7 +49,7 @@ public Struct1 getProp1() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java index 15dd505..7ff0e1c 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct1InterfaceJniServiceFactory() @NonNull private static SameStruct1InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct1InterfaceJniServiceFactory t = new SameStruct1InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java index 22f3d38..311cbe5 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct1InterfaceJniServiceStarter { public static ISameStruct1Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct1InterfaceJniServiceFactory factory = SameStruct1InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); return SameStruct1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java index a1b7f15..1cea6e5 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java @@ -66,7 +66,7 @@ public Struct2 getProp2() @Override public Struct1 func1(Struct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -79,7 +79,7 @@ public CompletableFuture func1Async(Struct1 param1) { @Override public Struct1 func2(Struct1 param1, Struct2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java index 66a6dcb..1876568 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SameStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SameStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SameStruct2InterfaceJniServiceFactory() @NonNull private static SameStruct2InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SameStruct2InterfaceJniServiceFactory t = new SameStruct2InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java index c96a9b8..94522bc 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SameStruct2InterfaceJniServiceStarter { public static ISameStruct2Interface start(Context context) { stop(context); androidService = new Intent(context, SameStruct2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SameStruct2InterfaceJniServiceFactory factory = SameStruct2InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); return SameStruct2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java index 5356408..9be726f 100644 --- a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java @@ -87,7 +87,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig1.setText("sig1"); bSig1.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig1 "); + Log.i(TAG, "broadcasting singal sig1 "); Struct1 param1 = new Struct1(); mBackend.fireSig1(param1); }); @@ -152,7 +152,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, SameStruct1InterfaceServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = SameStruct1InterfaceServiceAdapter.setService(SameStruct1InterfaceServiceFactory.get()); mBackend.addEventListener(this); } @@ -178,27 +178,26 @@ protected void onDestroy() public void onProp1Changed(Struct1 newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onSig1(Struct1 param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java index a5e1d80..6283085 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/EmptyInterfaceClient.java @@ -117,7 +117,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java index 687e67c..e9d9e28 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java @@ -119,7 +119,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java index afa2b36..a8c3cf0 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java @@ -117,7 +117,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java index 20e58ea..eb28f0b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClient.java @@ -119,7 +119,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java index b231478..424d7a6 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.java @@ -126,7 +126,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java index 8c65493..49363b5 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java @@ -125,7 +125,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java index b172b68..5a8cb1b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java @@ -117,7 +117,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java index 5ea87a3..3968576 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: EmptyInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: EmptyInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private EmptyInterfaceServiceFactory() @NonNull private static EmptyInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); EmptyInterfaceServiceFactory t = new EmptyInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java index 0b32d98..7465216 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class EmptyInterfaceServiceStarter { public static IEmptyInterface start(Context context) { stop(context); androidService = new Intent(context, EmptyInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); EmptyInterfaceServiceFactory factory = EmptyInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for EmptyInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for EmptyInterfaceServiceFactory"); return EmptyInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java index b76a0dc..2102951 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NoOperationsInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NoOperationsInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NoOperationsInterfaceServiceFactory() @NonNull private static NoOperationsInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NoOperationsInterfaceServiceFactory t = new NoOperationsInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java index e95b214..75831a6 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class NoOperationsInterfaceServiceStarter { public static INoOperationsInterface start(Context context) { stop(context); androidService = new Intent(context, NoOperationsInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NoOperationsInterfaceServiceFactory factory = NoOperationsInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for NoOperationsInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for NoOperationsInterfaceServiceFactory"); return NoOperationsInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java index 39468ba..0d81395 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NoPropertiesInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NoPropertiesInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NoPropertiesInterfaceServiceFactory() @NonNull private static NoPropertiesInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NoPropertiesInterfaceServiceFactory t = new NoPropertiesInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java index 3b33dd1..a4eaf24 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class NoPropertiesInterfaceServiceStarter { public static INoPropertiesInterface start(Context context) { stop(context); androidService = new Intent(context, NoPropertiesInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NoPropertiesInterfaceServiceFactory factory = NoPropertiesInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for NoPropertiesInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for NoPropertiesInterfaceServiceFactory"); return NoPropertiesInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java index 7c78ec6..75b793c 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NoSignalsInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NoSignalsInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NoSignalsInterfaceServiceFactory() @NonNull private static NoSignalsInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NoSignalsInterfaceServiceFactory t = new NoSignalsInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java index 7f732d8..ee7878f 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class NoSignalsInterfaceServiceStarter { public static INoSignalsInterface start(Context context) { stop(context); androidService = new Intent(context, NoSignalsInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NoSignalsInterfaceServiceFactory factory = NoSignalsInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for NoSignalsInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for NoSignalsInterfaceServiceFactory"); return NoSignalsInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java index 7c7db5e..dc93d53 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SimpleArrayInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SimpleArrayInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SimpleArrayInterfaceServiceFactory() @NonNull private static SimpleArrayInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SimpleArrayInterfaceServiceFactory t = new SimpleArrayInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java index 5e329d5..8923058 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SimpleArrayInterfaceServiceStarter { public static ISimpleArrayInterface start(Context context) { stop(context); androidService = new Intent(context, SimpleArrayInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SimpleArrayInterfaceServiceFactory factory = SimpleArrayInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SimpleArrayInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SimpleArrayInterfaceServiceFactory"); return SimpleArrayInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java index 0cd2498..aebf174 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SimpleInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: SimpleInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SimpleInterfaceServiceFactory() @NonNull private static SimpleInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SimpleInterfaceServiceFactory t = new SimpleInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java index 8c3a916..9cdf51e 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class SimpleInterfaceServiceStarter { public static ISimpleInterface start(Context context) { stop(context); androidService = new Intent(context, SimpleInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SimpleInterfaceServiceFactory factory = SimpleInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for SimpleInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for SimpleInterfaceServiceFactory"); return SimpleInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java index 4703f5e..fb6398e 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: VoidInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: VoidInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private VoidInterfaceServiceFactory() @NonNull private static VoidInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); VoidInterfaceServiceFactory t = new VoidInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java index 2834d9a..1b78ee1 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class VoidInterfaceServiceStarter { public static IVoidInterface start(Context context) { stop(context); androidService = new Intent(context, VoidInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); VoidInterfaceServiceFactory factory = VoidInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for VoidInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for VoidInterfaceServiceFactory"); return VoidInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java index dd3bb15..9b710c6 100644 --- a/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java @@ -67,7 +67,7 @@ protected void onCreate(Bundle savedInstanceState) { bFuncVoid.setText("funcVoid"); bFuncVoid.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD funcVoid "); + Log.i(TAG, "CALLING METHOD funcVoid "); CompletableFuture method_res = mClient.funcVoidAsync().thenApply( i -> { @@ -172,12 +172,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new VoidInterfaceClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -190,18 +190,18 @@ public void onSigVoid() { String text = "Signal sigVoid "; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java index f88099d..ed08fa1 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java @@ -33,7 +33,7 @@ public NoPropertiesInterfaceService() @Override public void funcVoid() { - Log.w(TAG, "request method funcVoid called, returnig default"); + Log.i(TAG, "request method funcVoid called, returnig default"); return ; } @@ -46,7 +46,7 @@ public CompletableFuture funcVoidAsync() { @Override public boolean funcBool(boolean paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return false; } diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java index dc58d10..e276b58 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java @@ -75,7 +75,7 @@ public int getPropInt() @Override public void funcVoid() { - Log.w(TAG, "request method funcVoid called, returnig default"); + Log.i(TAG, "request method funcVoid called, returnig default"); return ; } @@ -88,7 +88,7 @@ public CompletableFuture funcVoidAsync() { @Override public boolean funcBool(boolean paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return false; } diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java index 5f8fa99..a4e864e 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java @@ -222,7 +222,7 @@ public String getPropReadOnlyString() @Override public boolean[] funcBool(boolean[] paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return new boolean[]{}; } @@ -235,7 +235,7 @@ public CompletableFuture funcBoolAsync(boolean[] paramBool) { @Override public int[] funcInt(int[] paramInt) { - Log.w(TAG, "request method funcInt called, returnig default"); + Log.i(TAG, "request method funcInt called, returnig default"); return new int[]{}; } @@ -248,7 +248,7 @@ public CompletableFuture funcIntAsync(int[] paramInt) { @Override public int[] funcInt32(int[] paramInt32) { - Log.w(TAG, "request method funcInt32 called, returnig default"); + Log.i(TAG, "request method funcInt32 called, returnig default"); return new int[]{}; } @@ -261,7 +261,7 @@ public CompletableFuture funcInt32Async(int[] paramInt32) { @Override public long[] funcInt64(long[] paramInt64) { - Log.w(TAG, "request method funcInt64 called, returnig default"); + Log.i(TAG, "request method funcInt64 called, returnig default"); return new long[]{}; } @@ -274,7 +274,7 @@ public CompletableFuture funcInt64Async(long[] paramInt64) { @Override public float[] funcFloat(float[] paramFloat) { - Log.w(TAG, "request method funcFloat called, returnig default"); + Log.i(TAG, "request method funcFloat called, returnig default"); return new float[]{}; } @@ -287,7 +287,7 @@ public CompletableFuture funcFloatAsync(float[] paramFloat) { @Override public float[] funcFloat32(float[] paramFloat32) { - Log.w(TAG, "request method funcFloat32 called, returnig default"); + Log.i(TAG, "request method funcFloat32 called, returnig default"); return new float[]{}; } @@ -300,7 +300,7 @@ public CompletableFuture funcFloat32Async(float[] paramFloat32) { @Override public double[] funcFloat64(double[] paramFloat) { - Log.w(TAG, "request method funcFloat64 called, returnig default"); + Log.i(TAG, "request method funcFloat64 called, returnig default"); return new double[]{}; } @@ -313,7 +313,7 @@ public CompletableFuture funcFloat64Async(double[] paramFloat) { @Override public String[] funcString(String[] paramString) { - Log.w(TAG, "request method funcString called, returnig default"); + Log.i(TAG, "request method funcString called, returnig default"); return new String[]{}; } diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java index ccdeae2..7608d36 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java @@ -201,7 +201,7 @@ public String getPropString() @Override public void funcNoReturnValue(boolean paramBool) { - Log.w(TAG, "request method funcNoReturnValue called, returnig default"); + Log.i(TAG, "request method funcNoReturnValue called, returnig default"); return ; } @@ -214,7 +214,7 @@ public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { @Override public boolean funcNoParams() { - Log.w(TAG, "request method funcNoParams called, returnig default"); + Log.i(TAG, "request method funcNoParams called, returnig default"); return false; } @@ -227,7 +227,7 @@ public CompletableFuture funcNoParamsAsync() { @Override public boolean funcBool(boolean paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return false; } @@ -240,7 +240,7 @@ public CompletableFuture funcBoolAsync(boolean paramBool) { @Override public int funcInt(int paramInt) { - Log.w(TAG, "request method funcInt called, returnig default"); + Log.i(TAG, "request method funcInt called, returnig default"); return 0; } @@ -253,7 +253,7 @@ public CompletableFuture funcIntAsync(int paramInt) { @Override public int funcInt32(int paramInt32) { - Log.w(TAG, "request method funcInt32 called, returnig default"); + Log.i(TAG, "request method funcInt32 called, returnig default"); return 0; } @@ -266,7 +266,7 @@ public CompletableFuture funcInt32Async(int paramInt32) { @Override public long funcInt64(long paramInt64) { - Log.w(TAG, "request method funcInt64 called, returnig default"); + Log.i(TAG, "request method funcInt64 called, returnig default"); return 0L; } @@ -279,7 +279,7 @@ public CompletableFuture funcInt64Async(long paramInt64) { @Override public float funcFloat(float paramFloat) { - Log.w(TAG, "request method funcFloat called, returnig default"); + Log.i(TAG, "request method funcFloat called, returnig default"); return 0.0f; } @@ -292,7 +292,7 @@ public CompletableFuture funcFloatAsync(float paramFloat) { @Override public float funcFloat32(float paramFloat32) { - Log.w(TAG, "request method funcFloat32 called, returnig default"); + Log.i(TAG, "request method funcFloat32 called, returnig default"); return 0.0f; } @@ -305,7 +305,7 @@ public CompletableFuture funcFloat32Async(float paramFloat32) { @Override public double funcFloat64(double paramFloat) { - Log.w(TAG, "request method funcFloat64 called, returnig default"); + Log.i(TAG, "request method funcFloat64 called, returnig default"); return 0.0; } @@ -318,7 +318,7 @@ public CompletableFuture funcFloat64Async(double paramFloat) { @Override public String funcString(String paramString) { - Log.w(TAG, "request method funcString called, returnig default"); + Log.i(TAG, "request method funcString called, returnig default"); return new String(); } diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java index 6b259fb..9fc0c9c 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java @@ -33,7 +33,7 @@ public VoidInterfaceService() @Override public void funcVoid() { - Log.w(TAG, "request method funcVoid called, returnig default"); + Log.i(TAG, "request method funcVoid called, returnig default"); return ; } diff --git a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java index ebbb128..d565c93 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java @@ -48,7 +48,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new EmptyInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -62,7 +62,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java index 9acb14e..18ef3a2 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java @@ -74,7 +74,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NoOperationsInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -88,7 +88,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -96,25 +96,25 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(boolean newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } @Override public void onSigVoid() { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigVoid "); nativeOnSigVoid(); } @Override public void onSigBool(boolean paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } private native void nativeOnPropBoolChanged(boolean propBool); diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java index 214a0c4..01c8be3 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java @@ -84,7 +84,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NoPropertiesInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -98,7 +98,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -106,13 +106,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onSigVoid() { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigVoid "); nativeOnSigVoid(); } @Override public void onSigBool(boolean paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } private native void nativeOnSigVoid(); diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java index 6cdc249..1253779 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java @@ -110,7 +110,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NoSignalsInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -124,7 +124,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -132,13 +132,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(boolean newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } private native void nativeOnPropBoolChanged(boolean propBool); diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java index 4299805..25ee331 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java @@ -309,7 +309,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SimpleArrayInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -323,7 +323,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -331,103 +331,103 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(boolean[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(int[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } @Override public void onPropInt32Changed(int[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropInt32Changed(newValue); } @Override public void onPropInt64Changed(long[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropInt64Changed(newValue); } @Override public void onPropFloatChanged(float[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloatChanged(newValue); } @Override public void onPropFloat32Changed(float[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloat32Changed(newValue); } @Override public void onPropFloat64Changed(double[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloat64Changed(newValue); } @Override public void onPropStringChanged(String[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropStringChanged(newValue); } @Override public void onPropReadOnlyStringChanged(String newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropReadOnlyStringChanged(newValue); } @Override public void onSigBool(boolean[] paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } @Override public void onSigInt(int[] paramInt) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); nativeOnSigInt(paramInt); } @Override public void onSigInt32(int[] paramInt32) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); nativeOnSigInt32(paramInt32); } @Override public void onSigInt64(long[] paramInt64) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); nativeOnSigInt64(paramInt64); } @Override public void onSigFloat(float[] paramFloat) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); nativeOnSigFloat(paramFloat); } @Override public void onSigFloat32(float[] paramFloa32) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloa32); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloa32); nativeOnSigFloat32(paramFloa32); } @Override public void onSigFloat64(double[] paramFloat64) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); nativeOnSigFloat64(paramFloat64); } @Override public void onSigString(String[] paramString) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); nativeOnSigString(paramString); } private native void nativeOnPropBoolChanged(boolean[] propBool); diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java index 33c9054..b2ddc7a 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java @@ -332,7 +332,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new SimpleInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -346,7 +346,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -354,97 +354,97 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(boolean newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } @Override public void onPropInt32Changed(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropInt32Changed(newValue); } @Override public void onPropInt64Changed(long newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropInt64Changed(newValue); } @Override public void onPropFloatChanged(float newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloatChanged(newValue); } @Override public void onPropFloat32Changed(float newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloat32Changed(newValue); } @Override public void onPropFloat64Changed(double newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloat64Changed(newValue); } @Override public void onPropStringChanged(String newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropStringChanged(newValue); } @Override public void onSigBool(boolean paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } @Override public void onSigInt(int paramInt) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); nativeOnSigInt(paramInt); } @Override public void onSigInt32(int paramInt32) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); nativeOnSigInt32(paramInt32); } @Override public void onSigInt64(long paramInt64) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); nativeOnSigInt64(paramInt64); } @Override public void onSigFloat(float paramFloat) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); nativeOnSigFloat(paramFloat); } @Override public void onSigFloat32(float paramFloat32) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloat32); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloat32); nativeOnSigFloat32(paramFloat32); } @Override public void onSigFloat64(double paramFloat64) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); nativeOnSigFloat64(paramFloat64); } @Override public void onSigString(String paramString) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); nativeOnSigString(paramString); } private native void nativeOnPropBoolChanged(boolean propBool); diff --git a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java index 94df48a..57a8b5f 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java @@ -66,7 +66,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new VoidInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -80,7 +80,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -88,7 +88,7 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onSigVoid() { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigVoid "); nativeOnSigVoid(); } private native void nativeOnSigVoid(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java index 12774de..35af06e 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: EmptyInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: EmptyInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private EmptyInterfaceJniServiceFactory() @NonNull private static EmptyInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); EmptyInterfaceJniServiceFactory t = new EmptyInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java index 9259461..c94b51b 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class EmptyInterfaceJniServiceStarter { public static IEmptyInterface start(Context context) { stop(context); androidService = new Intent(context, EmptyInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); EmptyInterfaceJniServiceFactory factory = EmptyInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for EmptyInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for EmptyInterfaceJniServiceFactory"); return EmptyInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java index 5a89199..8cf3c1e 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NoOperationsInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NoOperationsInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NoOperationsInterfaceJniServiceFactory() @NonNull private static NoOperationsInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NoOperationsInterfaceJniServiceFactory t = new NoOperationsInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java index b59a90a..f8ac629 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class NoOperationsInterfaceJniServiceStarter { public static INoOperationsInterface start(Context context) { stop(context); androidService = new Intent(context, NoOperationsInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NoOperationsInterfaceJniServiceFactory factory = NoOperationsInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NoOperationsInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for NoOperationsInterfaceJniServiceFactory"); return NoOperationsInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java index ae66365..308761c 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java @@ -32,7 +32,7 @@ public NoPropertiesInterfaceJniService() @Override public void funcVoid() { - Log.w(TAG, "request method funcVoid called, will call native"); + Log.i(TAG, "request method funcVoid called, will call native"); nativeFuncVoid(); } @@ -45,7 +45,7 @@ public CompletableFuture funcVoidAsync() { @Override public boolean funcBool(boolean paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java index 234d254..9fe3379 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NoPropertiesInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NoPropertiesInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NoPropertiesInterfaceJniServiceFactory() @NonNull private static NoPropertiesInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NoPropertiesInterfaceJniServiceFactory t = new NoPropertiesInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java index 60e249c..b92c420 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class NoPropertiesInterfaceJniServiceStarter { public static INoPropertiesInterface start(Context context) { stop(context); androidService = new Intent(context, NoPropertiesInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NoPropertiesInterfaceJniServiceFactory factory = NoPropertiesInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NoPropertiesInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for NoPropertiesInterfaceJniServiceFactory"); return NoPropertiesInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java index aaa8e6a..11a652d 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java @@ -62,7 +62,7 @@ public int getPropInt() @Override public void funcVoid() { - Log.w(TAG, "request method funcVoid called, will call native"); + Log.i(TAG, "request method funcVoid called, will call native"); nativeFuncVoid(); } @@ -75,7 +75,7 @@ public CompletableFuture funcVoidAsync() { @Override public boolean funcBool(boolean paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java index a2c9bc8..7862698 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NoSignalsInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NoSignalsInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NoSignalsInterfaceJniServiceFactory() @NonNull private static NoSignalsInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NoSignalsInterfaceJniServiceFactory t = new NoSignalsInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java index ffbd1b1..89caaf9 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class NoSignalsInterfaceJniServiceStarter { public static INoSignalsInterface start(Context context) { stop(context); androidService = new Intent(context, NoSignalsInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NoSignalsInterfaceJniServiceFactory factory = NoSignalsInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NoSignalsInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for NoSignalsInterfaceJniServiceFactory"); return NoSignalsInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java index f5c4cb6..2a9a792 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java @@ -167,7 +167,7 @@ public String getPropReadOnlyString() @Override public boolean[] funcBool(boolean[] paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } @@ -180,7 +180,7 @@ public CompletableFuture funcBoolAsync(boolean[] paramBool) { @Override public int[] funcInt(int[] paramInt) { - Log.w(TAG, "request method funcInt called, will call native"); + Log.i(TAG, "request method funcInt called, will call native"); return nativeFuncInt(paramInt); } @@ -193,7 +193,7 @@ public CompletableFuture funcIntAsync(int[] paramInt) { @Override public int[] funcInt32(int[] paramInt32) { - Log.w(TAG, "request method funcInt32 called, will call native"); + Log.i(TAG, "request method funcInt32 called, will call native"); return nativeFuncInt32(paramInt32); } @@ -206,7 +206,7 @@ public CompletableFuture funcInt32Async(int[] paramInt32) { @Override public long[] funcInt64(long[] paramInt64) { - Log.w(TAG, "request method funcInt64 called, will call native"); + Log.i(TAG, "request method funcInt64 called, will call native"); return nativeFuncInt64(paramInt64); } @@ -219,7 +219,7 @@ public CompletableFuture funcInt64Async(long[] paramInt64) { @Override public float[] funcFloat(float[] paramFloat) { - Log.w(TAG, "request method funcFloat called, will call native"); + Log.i(TAG, "request method funcFloat called, will call native"); return nativeFuncFloat(paramFloat); } @@ -232,7 +232,7 @@ public CompletableFuture funcFloatAsync(float[] paramFloat) { @Override public float[] funcFloat32(float[] paramFloat32) { - Log.w(TAG, "request method funcFloat32 called, will call native"); + Log.i(TAG, "request method funcFloat32 called, will call native"); return nativeFuncFloat32(paramFloat32); } @@ -245,7 +245,7 @@ public CompletableFuture funcFloat32Async(float[] paramFloat32) { @Override public double[] funcFloat64(double[] paramFloat) { - Log.w(TAG, "request method funcFloat64 called, will call native"); + Log.i(TAG, "request method funcFloat64 called, will call native"); return nativeFuncFloat64(paramFloat); } @@ -258,7 +258,7 @@ public CompletableFuture funcFloat64Async(double[] paramFloat) { @Override public String[] funcString(String[] paramString) { - Log.w(TAG, "request method funcString called, will call native"); + Log.i(TAG, "request method funcString called, will call native"); return nativeFuncString(paramString); } diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java index 306ef8e..37749d4 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SimpleArrayInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SimpleArrayInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SimpleArrayInterfaceJniServiceFactory() @NonNull private static SimpleArrayInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SimpleArrayInterfaceJniServiceFactory t = new SimpleArrayInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java index ad24360..adb27aa 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SimpleArrayInterfaceJniServiceStarter { public static ISimpleArrayInterface start(Context context) { stop(context); androidService = new Intent(context, SimpleArrayInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SimpleArrayInterfaceJniServiceFactory factory = SimpleArrayInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SimpleArrayInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SimpleArrayInterfaceJniServiceFactory"); return SimpleArrayInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java index f3f6c96..40f756f 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java @@ -152,7 +152,7 @@ public String getPropString() @Override public void funcNoReturnValue(boolean paramBool) { - Log.w(TAG, "request method funcNoReturnValue called, will call native"); + Log.i(TAG, "request method funcNoReturnValue called, will call native"); nativeFuncNoReturnValue(paramBool); } @@ -165,7 +165,7 @@ public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { @Override public boolean funcNoParams() { - Log.w(TAG, "request method funcNoParams called, will call native"); + Log.i(TAG, "request method funcNoParams called, will call native"); return nativeFuncNoParams(); } @@ -178,7 +178,7 @@ public CompletableFuture funcNoParamsAsync() { @Override public boolean funcBool(boolean paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } @@ -191,7 +191,7 @@ public CompletableFuture funcBoolAsync(boolean paramBool) { @Override public int funcInt(int paramInt) { - Log.w(TAG, "request method funcInt called, will call native"); + Log.i(TAG, "request method funcInt called, will call native"); return nativeFuncInt(paramInt); } @@ -204,7 +204,7 @@ public CompletableFuture funcIntAsync(int paramInt) { @Override public int funcInt32(int paramInt32) { - Log.w(TAG, "request method funcInt32 called, will call native"); + Log.i(TAG, "request method funcInt32 called, will call native"); return nativeFuncInt32(paramInt32); } @@ -217,7 +217,7 @@ public CompletableFuture funcInt32Async(int paramInt32) { @Override public long funcInt64(long paramInt64) { - Log.w(TAG, "request method funcInt64 called, will call native"); + Log.i(TAG, "request method funcInt64 called, will call native"); return nativeFuncInt64(paramInt64); } @@ -230,7 +230,7 @@ public CompletableFuture funcInt64Async(long paramInt64) { @Override public float funcFloat(float paramFloat) { - Log.w(TAG, "request method funcFloat called, will call native"); + Log.i(TAG, "request method funcFloat called, will call native"); return nativeFuncFloat(paramFloat); } @@ -243,7 +243,7 @@ public CompletableFuture funcFloatAsync(float paramFloat) { @Override public float funcFloat32(float paramFloat32) { - Log.w(TAG, "request method funcFloat32 called, will call native"); + Log.i(TAG, "request method funcFloat32 called, will call native"); return nativeFuncFloat32(paramFloat32); } @@ -256,7 +256,7 @@ public CompletableFuture funcFloat32Async(float paramFloat32) { @Override public double funcFloat64(double paramFloat) { - Log.w(TAG, "request method funcFloat64 called, will call native"); + Log.i(TAG, "request method funcFloat64 called, will call native"); return nativeFuncFloat64(paramFloat); } @@ -269,7 +269,7 @@ public CompletableFuture funcFloat64Async(double paramFloat) { @Override public String funcString(String paramString) { - Log.w(TAG, "request method funcString called, will call native"); + Log.i(TAG, "request method funcString called, will call native"); return nativeFuncString(paramString); } diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java index e62ffa0..b855c59 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: SimpleInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: SimpleInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private SimpleInterfaceJniServiceFactory() @NonNull private static SimpleInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); SimpleInterfaceJniServiceFactory t = new SimpleInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java index 4fc7a65..b051a18 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class SimpleInterfaceJniServiceStarter { public static ISimpleInterface start(Context context) { stop(context); androidService = new Intent(context, SimpleInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); SimpleInterfaceJniServiceFactory factory = SimpleInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for SimpleInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for SimpleInterfaceJniServiceFactory"); return SimpleInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java index 7a6bae8..087cede 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java @@ -32,7 +32,7 @@ public VoidInterfaceJniService() @Override public void funcVoid() { - Log.w(TAG, "request method funcVoid called, will call native"); + Log.i(TAG, "request method funcVoid called, will call native"); nativeFuncVoid(); } diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java index 0ec88e5..501faad 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: VoidInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: VoidInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private VoidInterfaceJniServiceFactory() @NonNull private static VoidInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); VoidInterfaceJniServiceFactory t = new VoidInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java index 87c2a4d..e07f053 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class VoidInterfaceJniServiceStarter { public static IVoidInterface start(Context context) { stop(context); androidService = new Intent(context, VoidInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); VoidInterfaceJniServiceFactory factory = VoidInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for VoidInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for VoidInterfaceJniServiceFactory"); return VoidInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java index e015a42..4853104 100644 --- a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java @@ -74,7 +74,7 @@ protected void onCreate(Bundle savedInstanceState) { bSigVoid.setText("sigVoid"); bSigVoid.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sigVoid "); + Log.i(TAG, "broadcasting singal sigVoid "); mBackend.fireSigVoid(); }); bSigVoid.setBackgroundColor(Color.GREEN); @@ -138,7 +138,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, VoidInterfaceServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = VoidInterfaceServiceAdapter.setService(VoidInterfaceServiceFactory.get()); mBackend.addEventListener(this); } @@ -165,20 +165,19 @@ public void onSigVoid() { String text = "Signal sigVoid "; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java index 5ed04bb..84bba02 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java @@ -142,7 +142,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java index ac6dd39..7436d3e 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.java @@ -132,7 +132,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java index 540a21c..4a74fbb 100644 --- a/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java @@ -129,7 +129,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java index b878e01..08fc7af 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: StructArray2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: StructArray2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private StructArray2InterfaceServiceFactory() @NonNull private static StructArray2InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); StructArray2InterfaceServiceFactory t = new StructArray2InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java index 7db17af..fce07c2 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class StructArray2InterfaceServiceStarter { public static IStructArray2Interface start(Context context) { stop(context); androidService = new Intent(context, StructArray2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); StructArray2InterfaceServiceFactory factory = StructArray2InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for StructArray2InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for StructArray2InterfaceServiceFactory"); return StructArray2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java index c36fccd..df5af01 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: StructArrayInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: StructArrayInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private StructArrayInterfaceServiceFactory() @NonNull private static StructArrayInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); StructArrayInterfaceServiceFactory t = new StructArrayInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java index cc982df..e66a765 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class StructArrayInterfaceServiceStarter { public static IStructArrayInterface start(Context context) { stop(context); androidService = new Intent(context, StructArrayInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); StructArrayInterfaceServiceFactory factory = StructArrayInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for StructArrayInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for StructArrayInterfaceServiceFactory"); return StructArrayInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java index 302bc54..c6ded17 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: StructInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: StructInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private StructInterfaceServiceFactory() @NonNull private static StructInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); StructInterfaceServiceFactory t = new StructInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java index bc0bb0e..cbce398 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class StructInterfaceServiceStarter { public static IStructInterface start(Context context) { stop(context); androidService = new Intent(context, StructInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); StructInterfaceServiceFactory factory = StructInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for StructInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for StructInterfaceServiceFactory"); return StructInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java index d2e9d13..c09c525 100644 --- a/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java @@ -119,7 +119,7 @@ protected void onCreate(Bundle savedInstanceState) { bFuncBool.setText("funcBool"); bFuncBool.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD funcBool "); + Log.i(TAG, "CALLING METHOD funcBool "); StructBool paramBool = new StructBool(); CompletableFuture method_res = mClient.funcBoolAsync(paramBool).thenApply( @@ -134,7 +134,7 @@ protected void onCreate(Bundle savedInstanceState) { bFuncInt.setText("funcInt"); bFuncInt.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD funcInt "); + Log.i(TAG, "CALLING METHOD funcInt "); StructInt paramInt = new StructInt(); CompletableFuture method_res = mClient.funcIntAsync(paramInt).thenApply( @@ -149,7 +149,7 @@ protected void onCreate(Bundle savedInstanceState) { bFuncFloat.setText("funcFloat"); bFuncFloat.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD funcFloat "); + Log.i(TAG, "CALLING METHOD funcFloat "); StructFloat paramFloat = new StructFloat(); CompletableFuture method_res = mClient.funcFloatAsync(paramFloat).thenApply( @@ -164,7 +164,7 @@ protected void onCreate(Bundle savedInstanceState) { bFuncString.setText("funcString"); bFuncString.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD funcString "); + Log.i(TAG, "CALLING METHOD funcString "); StructString paramString = new StructString(); CompletableFuture method_res = mClient.funcStringAsync(paramString).thenApply( @@ -270,12 +270,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new StructInterfaceClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -287,64 +287,64 @@ private void initServiceConnection( String servicePackage) public void onPropBoolChanged(StructBool newValue) { outputTextViewProp.setText("Property from service: propBool " + newValue); - Log.w(TAG, "Property from service: propBool " + newValue); + Log.i(TAG, "Property from service: propBool " + newValue); } @Override public void onPropIntChanged(StructInt newValue) { outputTextViewProp.setText("Property from service: propInt " + newValue); - Log.w(TAG, "Property from service: propInt " + newValue); + Log.i(TAG, "Property from service: propInt " + newValue); } @Override public void onPropFloatChanged(StructFloat newValue) { outputTextViewProp.setText("Property from service: propFloat " + newValue); - Log.w(TAG, "Property from service: propFloat " + newValue); + Log.i(TAG, "Property from service: propFloat " + newValue); } @Override public void onPropStringChanged(StructString newValue) { outputTextViewProp.setText("Property from service: propString " + newValue); - Log.w(TAG, "Property from service: propString " + newValue); + Log.i(TAG, "Property from service: propString " + newValue); } @Override public void onSigBool(StructBool paramBool) { String text = "Signal sigBool "+ " " + paramBool; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSigInt(StructInt paramInt) { String text = "Signal sigInt "+ " " + paramInt; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSigFloat(StructFloat paramFloat) { String text = "Signal sigFloat "+ " " + paramFloat; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSigString(StructString paramString) { String text = "Signal sigString "+ " " + paramString; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java index 8db9607..407f4d5 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java @@ -153,7 +153,7 @@ public StructEnumWithArray getPropEnum() @Override public StructBool[] funcBool(StructBoolWithArray paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return new StructBool[]{}; } @@ -166,7 +166,7 @@ public CompletableFuture funcBoolAsync(StructBoolWithArray paramB @Override public StructInt[] funcInt(StructIntWithArray paramInt) { - Log.w(TAG, "request method funcInt called, returnig default"); + Log.i(TAG, "request method funcInt called, returnig default"); return new StructInt[]{}; } @@ -179,7 +179,7 @@ public CompletableFuture funcIntAsync(StructIntWithArray paramInt) @Override public StructFloat[] funcFloat(StructFloatWithArray paramFloat) { - Log.w(TAG, "request method funcFloat called, returnig default"); + Log.i(TAG, "request method funcFloat called, returnig default"); return new StructFloat[]{}; } @@ -192,7 +192,7 @@ public CompletableFuture funcFloatAsync(StructFloatWithArray par @Override public StructString[] funcString(StructStringWithArray paramString) { - Log.w(TAG, "request method funcString called, returnig default"); + Log.i(TAG, "request method funcString called, returnig default"); return new StructString[]{}; } @@ -205,7 +205,7 @@ public CompletableFuture funcStringAsync(StructStringWithArray @Override public Enum0[] funcEnum(StructEnumWithArray paramEnum) { - Log.w(TAG, "request method funcEnum called, returnig default"); + Log.i(TAG, "request method funcEnum called, returnig default"); return new Enum0[]{}; } diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java index 736b858..1a6c473 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java @@ -143,7 +143,7 @@ public Enum0[] getPropEnum() @Override public StructBool[] funcBool(StructBool[] paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return new StructBool[]{}; } @@ -156,7 +156,7 @@ public CompletableFuture funcBoolAsync(StructBool[] paramBool) { @Override public StructInt[] funcInt(StructInt[] paramInt) { - Log.w(TAG, "request method funcInt called, returnig default"); + Log.i(TAG, "request method funcInt called, returnig default"); return new StructInt[]{}; } @@ -169,7 +169,7 @@ public CompletableFuture funcIntAsync(StructInt[] paramInt) { @Override public StructFloat[] funcFloat(StructFloat[] paramFloat) { - Log.w(TAG, "request method funcFloat called, returnig default"); + Log.i(TAG, "request method funcFloat called, returnig default"); return new StructFloat[]{}; } @@ -182,7 +182,7 @@ public CompletableFuture funcFloatAsync(StructFloat[] paramFloat @Override public StructString[] funcString(StructString[] paramString) { - Log.w(TAG, "request method funcString called, returnig default"); + Log.i(TAG, "request method funcString called, returnig default"); return new StructString[]{}; } @@ -195,7 +195,7 @@ public CompletableFuture funcStringAsync(StructString[] paramSt @Override public Enum0[] funcEnum(Enum0[] paramEnum) { - Log.w(TAG, "request method funcEnum called, returnig default"); + Log.i(TAG, "request method funcEnum called, returnig default"); return new Enum0[]{}; } diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java index 5a4b95c..306315e 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java @@ -125,7 +125,7 @@ public StructString getPropString() @Override public StructBool funcBool(StructBool paramBool) { - Log.w(TAG, "request method funcBool called, returnig default"); + Log.i(TAG, "request method funcBool called, returnig default"); return new StructBool(); } @@ -138,7 +138,7 @@ public CompletableFuture funcBoolAsync(StructBool paramBool) { @Override public StructInt funcInt(StructInt paramInt) { - Log.w(TAG, "request method funcInt called, returnig default"); + Log.i(TAG, "request method funcInt called, returnig default"); return new StructInt(); } @@ -151,7 +151,7 @@ public CompletableFuture funcIntAsync(StructInt paramInt) { @Override public StructFloat funcFloat(StructFloat paramFloat) { - Log.w(TAG, "request method funcFloat called, returnig default"); + Log.i(TAG, "request method funcFloat called, returnig default"); return new StructFloat(); } @@ -164,7 +164,7 @@ public CompletableFuture funcFloatAsync(StructFloat paramFloat) { @Override public StructString funcString(StructString paramString) { - Log.w(TAG, "request method funcString called, returnig default"); + Log.i(TAG, "request method funcString called, returnig default"); return new StructString(); } diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java index 2151ceb..ef5b36a 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java @@ -223,7 +223,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new StructArray2InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -237,7 +237,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -245,55 +245,55 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(StructBoolWithArray newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(StructIntWithArray newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } @Override public void onPropFloatChanged(StructFloatWithArray newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloatChanged(newValue); } @Override public void onPropStringChanged(StructStringWithArray newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropStringChanged(newValue); } @Override public void onPropEnumChanged(StructEnumWithArray newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropEnumChanged(newValue); } @Override public void onSigBool(StructBoolWithArray paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } @Override public void onSigInt(StructIntWithArray paramInt) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); nativeOnSigInt(paramInt); } @Override public void onSigFloat(StructFloatWithArray paramFloat) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); nativeOnSigFloat(paramFloat); } @Override public void onSigString(StructStringWithArray paramString) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); nativeOnSigString(paramString); } private native void nativeOnPropBoolChanged(StructBoolWithArray propBool); diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java index 816892e..5036b9b 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java @@ -213,7 +213,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new StructArrayInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -227,7 +227,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -235,61 +235,61 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(StructBool[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(StructInt[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } @Override public void onPropFloatChanged(StructFloat[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloatChanged(newValue); } @Override public void onPropStringChanged(StructString[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropStringChanged(newValue); } @Override public void onPropEnumChanged(Enum0[] newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropEnumChanged(newValue); } @Override public void onSigBool(StructBool[] paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } @Override public void onSigInt(StructInt[] paramInt) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); nativeOnSigInt(paramInt); } @Override public void onSigFloat(StructFloat[] paramFloat) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); nativeOnSigFloat(paramFloat); } @Override public void onSigString(StructString[] paramString) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); nativeOnSigString(paramString); } @Override public void onSigEnum(Enum0[] paramEnum) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigEnum "+ " " + paramEnum); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigEnum "+ " " + paramEnum); nativeOnSigEnum(paramEnum); } private native void nativeOnPropBoolChanged(StructBool[] propBool); diff --git a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java index d90a719..f2ea2e0 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java @@ -180,7 +180,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new StructInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -194,7 +194,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -202,49 +202,49 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onPropBoolChanged(StructBool newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropBoolChanged(newValue); } @Override public void onPropIntChanged(StructInt newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropIntChanged(newValue); } @Override public void onPropFloatChanged(StructFloat newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropFloatChanged(newValue); } @Override public void onPropStringChanged(StructString newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnPropStringChanged(newValue); } @Override public void onSigBool(StructBool paramBool) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); nativeOnSigBool(paramBool); } @Override public void onSigInt(StructInt paramInt) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); nativeOnSigInt(paramInt); } @Override public void onSigFloat(StructFloat paramFloat) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); nativeOnSigFloat(paramFloat); } @Override public void onSigString(StructString paramString) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + Log.i(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); nativeOnSigString(paramString); } private native void nativeOnPropBoolChanged(StructBool propBool); diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java index 78444a9..f25ea9a 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java @@ -127,7 +127,7 @@ public StructEnumWithArray getPropEnum() @Override public StructBool[] funcBool(StructBoolWithArray paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } @@ -140,7 +140,7 @@ public CompletableFuture funcBoolAsync(StructBoolWithArray paramB @Override public StructInt[] funcInt(StructIntWithArray paramInt) { - Log.w(TAG, "request method funcInt called, will call native"); + Log.i(TAG, "request method funcInt called, will call native"); return nativeFuncInt(paramInt); } @@ -153,7 +153,7 @@ public CompletableFuture funcIntAsync(StructIntWithArray paramInt) @Override public StructFloat[] funcFloat(StructFloatWithArray paramFloat) { - Log.w(TAG, "request method funcFloat called, will call native"); + Log.i(TAG, "request method funcFloat called, will call native"); return nativeFuncFloat(paramFloat); } @@ -166,7 +166,7 @@ public CompletableFuture funcFloatAsync(StructFloatWithArray par @Override public StructString[] funcString(StructStringWithArray paramString) { - Log.w(TAG, "request method funcString called, will call native"); + Log.i(TAG, "request method funcString called, will call native"); return nativeFuncString(paramString); } @@ -179,7 +179,7 @@ public CompletableFuture funcStringAsync(StructStringWithArray @Override public Enum0[] funcEnum(StructEnumWithArray paramEnum) { - Log.w(TAG, "request method funcEnum called, will call native"); + Log.i(TAG, "request method funcEnum called, will call native"); return nativeFuncEnum(paramEnum); } diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java index add0ec1..9f1f0af 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: StructArray2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: StructArray2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private StructArray2InterfaceJniServiceFactory() @NonNull private static StructArray2InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); StructArray2InterfaceJniServiceFactory t = new StructArray2InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java index c13f66d..df4c587 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class StructArray2InterfaceJniServiceStarter { public static IStructArray2Interface start(Context context) { stop(context); androidService = new Intent(context, StructArray2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); StructArray2InterfaceJniServiceFactory factory = StructArray2InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for StructArray2InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for StructArray2InterfaceJniServiceFactory"); return StructArray2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java index 2b7d5f5..37083ef 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java @@ -117,7 +117,7 @@ public Enum0[] getPropEnum() @Override public StructBool[] funcBool(StructBool[] paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } @@ -130,7 +130,7 @@ public CompletableFuture funcBoolAsync(StructBool[] paramBool) { @Override public StructInt[] funcInt(StructInt[] paramInt) { - Log.w(TAG, "request method funcInt called, will call native"); + Log.i(TAG, "request method funcInt called, will call native"); return nativeFuncInt(paramInt); } @@ -143,7 +143,7 @@ public CompletableFuture funcIntAsync(StructInt[] paramInt) { @Override public StructFloat[] funcFloat(StructFloat[] paramFloat) { - Log.w(TAG, "request method funcFloat called, will call native"); + Log.i(TAG, "request method funcFloat called, will call native"); return nativeFuncFloat(paramFloat); } @@ -156,7 +156,7 @@ public CompletableFuture funcFloatAsync(StructFloat[] paramFloat @Override public StructString[] funcString(StructString[] paramString) { - Log.w(TAG, "request method funcString called, will call native"); + Log.i(TAG, "request method funcString called, will call native"); return nativeFuncString(paramString); } @@ -169,7 +169,7 @@ public CompletableFuture funcStringAsync(StructString[] paramSt @Override public Enum0[] funcEnum(Enum0[] paramEnum) { - Log.w(TAG, "request method funcEnum called, will call native"); + Log.i(TAG, "request method funcEnum called, will call native"); return nativeFuncEnum(paramEnum); } diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java index eb901f3..123bc7f 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: StructArrayInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: StructArrayInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private StructArrayInterfaceJniServiceFactory() @NonNull private static StructArrayInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); StructArrayInterfaceJniServiceFactory t = new StructArrayInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java index 2f1710c..94549de 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class StructArrayInterfaceJniServiceStarter { public static IStructArrayInterface start(Context context) { stop(context); androidService = new Intent(context, StructArrayInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); StructArrayInterfaceJniServiceFactory factory = StructArrayInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for StructArrayInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for StructArrayInterfaceJniServiceFactory"); return StructArrayInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java index 319bec1..91f4831 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java @@ -100,7 +100,7 @@ public StructString getPropString() @Override public StructBool funcBool(StructBool paramBool) { - Log.w(TAG, "request method funcBool called, will call native"); + Log.i(TAG, "request method funcBool called, will call native"); return nativeFuncBool(paramBool); } @@ -113,7 +113,7 @@ public CompletableFuture funcBoolAsync(StructBool paramBool) { @Override public StructInt funcInt(StructInt paramInt) { - Log.w(TAG, "request method funcInt called, will call native"); + Log.i(TAG, "request method funcInt called, will call native"); return nativeFuncInt(paramInt); } @@ -126,7 +126,7 @@ public CompletableFuture funcIntAsync(StructInt paramInt) { @Override public StructFloat funcFloat(StructFloat paramFloat) { - Log.w(TAG, "request method funcFloat called, will call native"); + Log.i(TAG, "request method funcFloat called, will call native"); return nativeFuncFloat(paramFloat); } @@ -139,7 +139,7 @@ public CompletableFuture funcFloatAsync(StructFloat paramFloat) { @Override public StructString funcString(StructString paramString) { - Log.w(TAG, "request method funcString called, will call native"); + Log.i(TAG, "request method funcString called, will call native"); return nativeFuncString(paramString); } diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java index ccbb34f..626309d 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: StructInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: StructInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private StructInterfaceJniServiceFactory() @NonNull private static StructInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); StructInterfaceJniServiceFactory t = new StructInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java index 44f63a6..ade42e3 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class StructInterfaceJniServiceStarter { public static IStructInterface start(Context context) { stop(context); androidService = new Intent(context, StructInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); StructInterfaceJniServiceFactory factory = StructInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for StructInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for StructInterfaceJniServiceFactory"); return StructInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java index 8ef99cd..0aa8cfa 100644 --- a/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java @@ -126,7 +126,7 @@ protected void onCreate(Bundle savedInstanceState) { bSigBool.setText("sigBool"); bSigBool.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sigBool "); + Log.i(TAG, "broadcasting singal sigBool "); StructBool paramBool = new StructBool(); mBackend.fireSigBool(paramBool); }); @@ -136,7 +136,7 @@ protected void onCreate(Bundle savedInstanceState) { bSigInt.setText("sigInt"); bSigInt.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sigInt "); + Log.i(TAG, "broadcasting singal sigInt "); StructInt paramInt = new StructInt(); mBackend.fireSigInt(paramInt); }); @@ -146,7 +146,7 @@ protected void onCreate(Bundle savedInstanceState) { bSigFloat.setText("sigFloat"); bSigFloat.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sigFloat "); + Log.i(TAG, "broadcasting singal sigFloat "); StructFloat paramFloat = new StructFloat(); mBackend.fireSigFloat(paramFloat); }); @@ -156,7 +156,7 @@ protected void onCreate(Bundle savedInstanceState) { bSigString.setText("sigString"); bSigString.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sigString "); + Log.i(TAG, "broadcasting singal sigString "); StructString paramString = new StructString(); mBackend.fireSigString(paramString); }); @@ -221,7 +221,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, StructInterfaceServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = StructInterfaceServiceAdapter.setService(StructInterfaceServiceFactory.get()); mBackend.addEventListener(this); } @@ -247,66 +247,65 @@ protected void onDestroy() public void onPropBoolChanged(StructBool newValue) { outputTextViewProp.setText("Property from service: propBool " + newValue); - Log.w(TAG, "Property from service: propBool " + newValue); + Log.i(TAG, "Property from service: propBool " + newValue); } @Override public void onPropIntChanged(StructInt newValue) { outputTextViewProp.setText("Property from service: propInt " + newValue); - Log.w(TAG, "Property from service: propInt " + newValue); + Log.i(TAG, "Property from service: propInt " + newValue); } @Override public void onPropFloatChanged(StructFloat newValue) { outputTextViewProp.setText("Property from service: propFloat " + newValue); - Log.w(TAG, "Property from service: propFloat " + newValue); + Log.i(TAG, "Property from service: propFloat " + newValue); } @Override public void onPropStringChanged(StructString newValue) { outputTextViewProp.setText("Property from service: propString " + newValue); - Log.w(TAG, "Property from service: propString " + newValue); + Log.i(TAG, "Property from service: propString " + newValue); } @Override public void onSigBool(StructBool paramBool) { String text = "Signal sigBool "+ " " + paramBool; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSigInt(StructInt paramInt) { String text = "Signal sigInt "+ " " + paramInt; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSigFloat(StructFloat paramFloat) { String text = "Signal sigFloat "+ " " + paramFloat; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSigString(StructString paramString) { String text = "Signal sigString "+ " " + paramString; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java index 2ce8d0e..9f1fbe2 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java @@ -121,7 +121,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java index a38ad87..d75253c 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java @@ -120,7 +120,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java index 71dd1cc..058c278 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java @@ -123,7 +123,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java index 4b488ef..561030b 100644 --- a/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java @@ -126,7 +126,7 @@ public void onServiceConnected(ComponentName name, IBinder serviceBinder) @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java index 46f25f9..0cb996f 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: ManyParamInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: ManyParamInterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private ManyParamInterfaceServiceFactory() @NonNull private static ManyParamInterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); ManyParamInterfaceServiceFactory t = new ManyParamInterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java index 1742363..37c5b01 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class ManyParamInterfaceServiceStarter { public static IManyParamInterface start(Context context) { stop(context); androidService = new Intent(context, ManyParamInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); ManyParamInterfaceServiceFactory factory = ManyParamInterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for ManyParamInterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for ManyParamInterfaceServiceFactory"); return ManyParamInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java index d7fc31e..b3b1182 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NestedStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NestedStruct1InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NestedStruct1InterfaceServiceFactory() @NonNull private static NestedStruct1InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NestedStruct1InterfaceServiceFactory t = new NestedStruct1InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java index 839e632..3b8d2bb 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class NestedStruct1InterfaceServiceStarter { public static INestedStruct1Interface start(Context context) { stop(context); androidService = new Intent(context, NestedStruct1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NestedStruct1InterfaceServiceFactory factory = NestedStruct1InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for NestedStruct1InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for NestedStruct1InterfaceServiceFactory"); return NestedStruct1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java index 7bf316e..9d9c565 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NestedStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NestedStruct2InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NestedStruct2InterfaceServiceFactory() @NonNull private static NestedStruct2InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NestedStruct2InterfaceServiceFactory t = new NestedStruct2InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java index fa4bd59..f89b087 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class NestedStruct2InterfaceServiceStarter { public static INestedStruct2Interface start(Context context) { stop(context); androidService = new Intent(context, NestedStruct2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NestedStruct2InterfaceServiceFactory factory = NestedStruct2InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for NestedStruct2InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for NestedStruct2InterfaceServiceFactory"); return NestedStruct2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java index ff596cb..c027313 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NestedStruct3InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: NestedStruct3InterfaceServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NestedStruct3InterfaceServiceFactory() @NonNull private static NestedStruct3InterfaceServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NestedStruct3InterfaceServiceFactory t = new NestedStruct3InterfaceServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java index 3f5d08b..7eb4433 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceStarter.java @@ -21,11 +21,11 @@ public class NestedStruct3InterfaceServiceStarter { public static INestedStruct3Interface start(Context context) { stop(context); androidService = new Intent(context, NestedStruct3InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NestedStruct3InterfaceServiceFactory factory = NestedStruct3InterfaceServiceFactory.get(); - Log.w(TAG, "starter: factory set for NestedStruct3InterfaceServiceFactory"); + Log.i(TAG, "starter: factory set for NestedStruct3InterfaceServiceFactory"); return NestedStruct3InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java index 1e4b5e9..9f94901 100644 --- a/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java @@ -115,7 +115,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc1.setText("func1"); bFunc1.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func1 "); + Log.i(TAG, "CALLING METHOD func1 "); int param1 = 1; CompletableFuture method_res = mClient.func1Async(param1).thenApply( @@ -130,7 +130,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc2.setText("func2"); bFunc2.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func2 "); + Log.i(TAG, "CALLING METHOD func2 "); int param1 = 1; int param2 = 1; CompletableFuture method_res @@ -146,7 +146,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc3.setText("func3"); bFunc3.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func3 "); + Log.i(TAG, "CALLING METHOD func3 "); int param1 = 1; int param2 = 1; int param3 = 1; @@ -163,7 +163,7 @@ protected void onCreate(Bundle savedInstanceState) { bFunc4.setText("func4"); bFunc4.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD func4 "); + Log.i(TAG, "CALLING METHOD func4 "); int param1 = 1; int param2 = 1; int param3 = 1; @@ -272,12 +272,12 @@ protected void onDestroy() private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new ManyParamInterfaceClient(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -289,64 +289,64 @@ private void initServiceConnection( String servicePackage) public void onProp1Changed(int newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onProp2Changed(int newValue) { outputTextViewProp.setText("Property from service: prop2 " + newValue); - Log.w(TAG, "Property from service: prop2 " + newValue); + Log.i(TAG, "Property from service: prop2 " + newValue); } @Override public void onProp3Changed(int newValue) { outputTextViewProp.setText("Property from service: prop3 " + newValue); - Log.w(TAG, "Property from service: prop3 " + newValue); + Log.i(TAG, "Property from service: prop3 " + newValue); } @Override public void onProp4Changed(int newValue) { outputTextViewProp.setText("Property from service: prop4 " + newValue); - Log.w(TAG, "Property from service: prop4 " + newValue); + Log.i(TAG, "Property from service: prop4 " + newValue); } @Override public void onSig1(int param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig2(int param1, int param2) { String text = "Signal sig2 "+ " " + param1+ " " + param2; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig3(int param1, int param2, int param3) { String text = "Signal sig3 "+ " " + param1+ " " + param2+ " " + param3; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig4(int param1, int param2, int param3, int param4) { String text = "Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java index a64092c..61d522f 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java @@ -117,7 +117,7 @@ public int getProp4() @Override public int func1(int param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return 0; } @@ -130,7 +130,7 @@ public CompletableFuture func1Async(int param1) { @Override public int func2(int param1, int param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return 0; } @@ -143,7 +143,7 @@ public CompletableFuture func2Async(int param1, int param2) { @Override public int func3(int param1, int param2, int param3) { - Log.w(TAG, "request method func3 called, returnig default"); + Log.i(TAG, "request method func3 called, returnig default"); return 0; } @@ -156,7 +156,7 @@ public CompletableFuture func3Async(int param1, int param2, int param3 @Override public int func4(int param1, int param2, int param3, int param4) { - Log.w(TAG, "request method func4 called, returnig default"); + Log.i(TAG, "request method func4 called, returnig default"); return 0; } diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java index 50db8ed..ff5ba00 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java @@ -56,7 +56,7 @@ public NestedStruct1 getProp1() @Override public void funcNoReturnValue(NestedStruct1 param1) { - Log.w(TAG, "request method funcNoReturnValue called, returnig default"); + Log.i(TAG, "request method funcNoReturnValue called, returnig default"); return ; } @@ -69,7 +69,7 @@ public CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1) { @Override public NestedStruct1 funcNoParams() { - Log.w(TAG, "request method funcNoParams called, returnig default"); + Log.i(TAG, "request method funcNoParams called, returnig default"); return new NestedStruct1(); } @@ -82,7 +82,7 @@ public CompletableFuture funcNoParamsAsync() { @Override public NestedStruct1 func1(NestedStruct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new NestedStruct1(); } diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java index 07d810d..2d6c1eb 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java @@ -79,7 +79,7 @@ public NestedStruct2 getProp2() @Override public NestedStruct1 func1(NestedStruct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new NestedStruct1(); } @@ -92,7 +92,7 @@ public CompletableFuture func1Async(NestedStruct1 param1) { @Override public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return new NestedStruct1(); } diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java index 351ab75..d3ddca9 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java @@ -102,7 +102,7 @@ public NestedStruct3 getProp3() @Override public NestedStruct1 func1(NestedStruct1 param1) { - Log.w(TAG, "request method func1 called, returnig default"); + Log.i(TAG, "request method func1 called, returnig default"); return new NestedStruct1(); } @@ -115,7 +115,7 @@ public CompletableFuture func1Async(NestedStruct1 param1) { @Override public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { - Log.w(TAG, "request method func2 called, returnig default"); + Log.i(TAG, "request method func2 called, returnig default"); return new NestedStruct1(); } @@ -128,7 +128,7 @@ public CompletableFuture func2Async(NestedStruct1 param1, Nested @Override public NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { - Log.w(TAG, "request method func3 called, returnig default"); + Log.i(TAG, "request method func3 called, returnig default"); return new NestedStruct1(); } diff --git a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java index 24c86f4..d4454b2 100644 --- a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java @@ -172,7 +172,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new ManyParamInterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -186,7 +186,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -194,49 +194,49 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onProp3Changed(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp3Changed(newValue); } @Override public void onProp4Changed(int newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp4Changed(newValue); } @Override public void onSig1(int param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(int param1, int param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } @Override public void onSig3(int param1, int param2, int param3) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param1+ " " + param2+ " " + param3); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param1+ " " + param2+ " " + param3); nativeOnSig3(param1, param2, param3); } @Override public void onSig4(int param1, int param2, int param3, int param4) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4); nativeOnSig4(param1, param2, param3, param4); } private native void nativeOnProp1Changed(int prop1); diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java index 636e01a..0122e1b 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -117,7 +117,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NestedStruct1InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -131,7 +131,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -139,13 +139,13 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(NestedStruct1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onSig1(NestedStruct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } private native void nativeOnProp1Changed(NestedStruct1 prop1); diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java index d06d858..14e3874 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java @@ -114,7 +114,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NestedStruct2InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -128,7 +128,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -136,25 +136,25 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(NestedStruct1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(NestedStruct2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onSig1(NestedStruct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(NestedStruct1 param1, NestedStruct2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } private native void nativeOnProp1Changed(NestedStruct1 prop1); diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java index 62c519f..7d126c4 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java @@ -147,7 +147,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String if (mMessengerClient == null) { mMessengerClient = new NestedStruct3InterfaceClient(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -161,7 +161,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -169,37 +169,37 @@ public void on_readyStatusChanged(boolean isReady) { @Override public void onProp1Changed(NestedStruct1 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp1Changed(newValue); } @Override public void onProp2Changed(NestedStruct2 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp2Changed(newValue); } @Override public void onProp3Changed(NestedStruct3 newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOnProp3Changed(newValue); } @Override public void onSig1(NestedStruct1 param1) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); nativeOnSig1(param1); } @Override public void onSig2(NestedStruct1 param1, NestedStruct2 param2) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param1+ " " + param2); nativeOnSig2(param1, param2); } @Override public void onSig3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { - Log.w(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param1+ " " + param2+ " " + param3); + Log.i(TAG, "NOTIFICATION from messenger client Signal sig3 "+ " " + param1+ " " + param2+ " " + param3); nativeOnSig3(param1, param2, param3); } private native void nativeOnProp1Changed(NestedStruct1 prop1); diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java index 7f8a00e..1919f7e 100644 --- a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java @@ -92,7 +92,7 @@ public int getProp4() @Override public int func1(int param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -105,7 +105,7 @@ public CompletableFuture func1Async(int param1) { @Override public int func2(int param1, int param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } @@ -118,7 +118,7 @@ public CompletableFuture func2Async(int param1, int param2) { @Override public int func3(int param1, int param2, int param3) { - Log.w(TAG, "request method func3 called, will call native"); + Log.i(TAG, "request method func3 called, will call native"); return nativeFunc3(param1, param2, param3); } @@ -131,7 +131,7 @@ public CompletableFuture func3Async(int param1, int param2, int param3 @Override public int func4(int param1, int param2, int param3, int param4) { - Log.w(TAG, "request method func4 called, will call native"); + Log.i(TAG, "request method func4 called, will call native"); return nativeFunc4(param1, param2, param3, param4); } diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java index c7ee965..10ffd4a 100644 --- a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: ManyParamInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: ManyParamInterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private ManyParamInterfaceJniServiceFactory() @NonNull private static ManyParamInterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); ManyParamInterfaceJniServiceFactory t = new ManyParamInterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java index 0cfd277..85750d7 100644 --- a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class ManyParamInterfaceJniServiceStarter { public static IManyParamInterface start(Context context) { stop(context); androidService = new Intent(context, ManyParamInterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); ManyParamInterfaceJniServiceFactory factory = ManyParamInterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for ManyParamInterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for ManyParamInterfaceJniServiceFactory"); return ManyParamInterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java index 3a18bd8..addea51 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java @@ -49,7 +49,7 @@ public NestedStruct1 getProp1() @Override public void funcNoReturnValue(NestedStruct1 param1) { - Log.w(TAG, "request method funcNoReturnValue called, will call native"); + Log.i(TAG, "request method funcNoReturnValue called, will call native"); nativeFuncNoReturnValue(param1); } @@ -62,7 +62,7 @@ public CompletableFuture funcNoReturnValueAsync(NestedStruct1 param1) { @Override public NestedStruct1 funcNoParams() { - Log.w(TAG, "request method funcNoParams called, will call native"); + Log.i(TAG, "request method funcNoParams called, will call native"); return nativeFuncNoParams(); } @@ -75,7 +75,7 @@ public CompletableFuture funcNoParamsAsync() { @Override public NestedStruct1 func1(NestedStruct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java index f4882cf..0365996 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NestedStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NestedStruct1InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NestedStruct1InterfaceJniServiceFactory() @NonNull private static NestedStruct1InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NestedStruct1InterfaceJniServiceFactory t = new NestedStruct1InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java index bb5141a..4eab1b3 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class NestedStruct1InterfaceJniServiceStarter { public static INestedStruct1Interface start(Context context) { stop(context); androidService = new Intent(context, NestedStruct1InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NestedStruct1InterfaceJniServiceFactory factory = NestedStruct1InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NestedStruct1InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for NestedStruct1InterfaceJniServiceFactory"); return NestedStruct1InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java index d510a25..f0f1710 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java @@ -66,7 +66,7 @@ public NestedStruct2 getProp2() @Override public NestedStruct1 func1(NestedStruct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -79,7 +79,7 @@ public CompletableFuture func1Async(NestedStruct1 param1) { @Override public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java index e9248a0..acbac93 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NestedStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NestedStruct2InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NestedStruct2InterfaceJniServiceFactory() @NonNull private static NestedStruct2InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NestedStruct2InterfaceJniServiceFactory t = new NestedStruct2InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java index 3de142a..b183560 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class NestedStruct2InterfaceJniServiceStarter { public static INestedStruct2Interface start(Context context) { stop(context); androidService = new Intent(context, NestedStruct2InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NestedStruct2InterfaceJniServiceFactory factory = NestedStruct2InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NestedStruct2InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for NestedStruct2InterfaceJniServiceFactory"); return NestedStruct2InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java index ca082d2..8141da8 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java @@ -83,7 +83,7 @@ public NestedStruct3 getProp3() @Override public NestedStruct1 func1(NestedStruct1 param1) { - Log.w(TAG, "request method func1 called, will call native"); + Log.i(TAG, "request method func1 called, will call native"); return nativeFunc1(param1); } @@ -96,7 +96,7 @@ public CompletableFuture func1Async(NestedStruct1 param1) { @Override public NestedStruct1 func2(NestedStruct1 param1, NestedStruct2 param2) { - Log.w(TAG, "request method func2 called, will call native"); + Log.i(TAG, "request method func2 called, will call native"); return nativeFunc2(param1, param2); } @@ -109,7 +109,7 @@ public CompletableFuture func2Async(NestedStruct1 param1, Nested @Override public NestedStruct1 func3(NestedStruct1 param1, NestedStruct2 param2, NestedStruct3 param3) { - Log.w(TAG, "request method func3 called, will call native"); + Log.i(TAG, "request method func3 called, will call native"); return nativeFunc3(param1, param2, param3); } diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java index 00df51c..da26a69 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceFactory.java @@ -43,7 +43,7 @@ public void onDestroy() { synchronized (this) { - Log.w("UE", "LIFECYCLE: NestedStruct3InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: NestedStruct3InterfaceJniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ private NestedStruct3InterfaceJniServiceFactory() @NonNull private static NestedStruct3InterfaceJniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); NestedStruct3InterfaceJniServiceFactory t = new NestedStruct3InterfaceJniServiceFactory(); t.start(); diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java index 046ef3b..4d2f74a 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniServiceStarter.java @@ -21,11 +21,11 @@ public class NestedStruct3InterfaceJniServiceStarter { public static INestedStruct3Interface start(Context context) { stop(context); androidService = new Intent(context, NestedStruct3InterfaceServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); NestedStruct3InterfaceJniServiceFactory factory = NestedStruct3InterfaceJniServiceFactory.get(); - Log.w(TAG, "starter: factory set for NestedStruct3InterfaceJniServiceFactory"); + Log.i(TAG, "starter: factory set for NestedStruct3InterfaceJniServiceFactory"); return NestedStruct3InterfaceServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public static void stop(Context context) { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java index 0bedd49..af7bff8 100644 --- a/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java @@ -118,7 +118,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig1.setText("sig1"); bSig1.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig1 "); + Log.i(TAG, "broadcasting singal sig1 "); int param1 = 1; mBackend.fireSig1(param1); }); @@ -128,7 +128,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig2.setText("sig2"); bSig2.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig2 "); + Log.i(TAG, "broadcasting singal sig2 "); int param1 = 1; int param2 = 1; mBackend.fireSig2(param1, param2); @@ -139,7 +139,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig3.setText("sig3"); bSig3.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig3 "); + Log.i(TAG, "broadcasting singal sig3 "); int param1 = 1; int param2 = 1; int param3 = 1; @@ -151,7 +151,7 @@ protected void onCreate(Bundle savedInstanceState) { bSig4.setText("sig4"); bSig4.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal sig4 "); + Log.i(TAG, "broadcasting singal sig4 "); int param1 = 1; int param2 = 1; int param3 = 1; @@ -219,7 +219,7 @@ protected void onStop() private void startMyService(){ stub_service = new Intent(this, ManyParamInterfaceServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = ManyParamInterfaceServiceAdapter.setService(ManyParamInterfaceServiceFactory.get()); mBackend.addEventListener(this); } @@ -245,66 +245,65 @@ protected void onDestroy() public void onProp1Changed(int newValue) { outputTextViewProp.setText("Property from service: prop1 " + newValue); - Log.w(TAG, "Property from service: prop1 " + newValue); + Log.i(TAG, "Property from service: prop1 " + newValue); } @Override public void onProp2Changed(int newValue) { outputTextViewProp.setText("Property from service: prop2 " + newValue); - Log.w(TAG, "Property from service: prop2 " + newValue); + Log.i(TAG, "Property from service: prop2 " + newValue); } @Override public void onProp3Changed(int newValue) { outputTextViewProp.setText("Property from service: prop3 " + newValue); - Log.w(TAG, "Property from service: prop3 " + newValue); + Log.i(TAG, "Property from service: prop3 " + newValue); } @Override public void onProp4Changed(int newValue) { outputTextViewProp.setText("Property from service: prop4 " + newValue); - Log.w(TAG, "Property from service: prop4 " + newValue); + Log.i(TAG, "Property from service: prop4 " + newValue); } @Override public void onSig1(int param1) { String text = "Signal sig1 "+ " " + param1; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig2(int param1, int param2) { String text = "Signal sig2 "+ " " + param1+ " " + param2; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig3(int param1, int param2, int param3) { String text = "Signal sig3 "+ " " + param1+ " " + param2+ " " + param3; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void onSig4(int param1, int param2, int param3, int param4) { String text = "Signal sig4 "+ " " + param1+ " " + param2+ " " + param3+ " " + param4; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } @Override public void on_readyStatusChanged(boolean isReady) { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } diff --git a/templates/android/client/client.java.tpl b/templates/android/client/client.java.tpl index 7d3e7d9..6361930 100644 --- a/templates/android/client/client.java.tpl +++ b/templates/android/client/client.java.tpl @@ -254,7 +254,7 @@ public class {{Camel .Interface.Name }}Client extends Abstract{{Camel .Interface @Override public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "onServiceDisconnected name=" + name); + Log.i(TAG, "onServiceDisconnected name=" + name); doCleanupForUnbinding("onServiceDisconnected name=" + name); } diff --git a/templates/android/service/implservicefactory.java.tpl b/templates/android/service/implservicefactory.java.tpl index 7b5fe05..008709c 100644 --- a/templates/android/service/implservicefactory.java.tpl +++ b/templates/android/service/implservicefactory.java.tpl @@ -43,7 +43,7 @@ public class {{Camel .Interface.Name}}ServiceFactory extends HandlerThread imple { synchronized (this) { - Log.w("UE", "LIFECYCLE: {{Camel .Interface.Name}}ServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); + Log.i("UE", "LIFECYCLE: {{Camel .Interface.Name}}ServiceFactory::onDestroy() - stop instance thread, service = " + m_Service); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ public class {{Camel .Interface.Name}}ServiceFactory extends HandlerThread imple @NonNull private static {{Camel .Interface.Name}}ServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); {{Camel .Interface.Name}}ServiceFactory t = new {{Camel .Interface.Name}}ServiceFactory(); t.start(); diff --git a/templates/android/service/implservicestarter.java.tpl b/templates/android/service/implservicestarter.java.tpl index e6df578..b1ba880 100644 --- a/templates/android/service/implservicestarter.java.tpl +++ b/templates/android/service/implservicestarter.java.tpl @@ -21,11 +21,11 @@ public class {{Camel .Interface.Name }}ServiceStarter { public static I{{Camel .Interface.Name }} start(Context context) { stop(context); androidService = new Intent(context, {{Camel .Interface.Name }}ServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); {{Camel .Interface.Name}}ServiceFactory factory = {{Camel .Interface.Name}}ServiceFactory.get(); - Log.w(TAG, "starter: factory set for {{Camel .Interface.Name}}ServiceFactory"); + Log.i(TAG, "starter: factory set for {{Camel .Interface.Name}}ServiceFactory"); return {{Camel .Interface.Name }}ServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public class {{Camel .Interface.Name }}ServiceStarter { { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/templates/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl index 7ea37df..e905c77 100644 --- a/templates/jnibridge/client/jnibridgeclient.java.tpl +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -140,7 +140,7 @@ public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interfa if (mMessengerClient == null) { mMessengerClient = new {{Camel .Interface.Name }}Client(ctx, connectionID); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { @@ -154,7 +154,7 @@ public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interfa @Override public void on_readyStatusChanged(boolean isReady) { - Log.w(TAG, "Connection state changed "+isReady); + Log.i(TAG, "Connection state changed "+isReady); nativeIsReady(isReady); } @@ -163,7 +163,7 @@ public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interfa @Override public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) { - Log.w(TAG, "NOTIFICATION from messenger client " + newValue); + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); nativeOn{{Camel .Name}}Changed(newValue); } {{- end }} @@ -172,7 +172,7 @@ public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interfa @Override public void on{{Camel .Name}}({{javaParams "" .Params}}) { - Log.w(TAG, "NOTIFICATION from messenger client Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}); + Log.i(TAG, "NOTIFICATION from messenger client Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}); nativeOn{{Camel .Name}}({{javaVars .Params }}); } {{- end }} diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl index 0db70af..839c879 100644 --- a/templates/jnibridge/service/jnibridgeservice.java.tpl +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -105,7 +105,7 @@ public class {{Camel .Interface.Name}}JniService extends Abstract{{Camel .Interf @Override public {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}) { - Log.w(TAG, "request method {{camel .Name}} called, will call native"); + Log.i(TAG, "request method {{camel .Name}} called, will call native"); {{if not .Return.IsVoid}}return{{ end }} native{{Camel .Name}}({{javaVars .Params }}); } diff --git a/templates/jnibridge/service/jnibridgeservicefactory.java.tpl b/templates/jnibridge/service/jnibridgeservicefactory.java.tpl index 87e2631..4a84c72 100644 --- a/templates/jnibridge/service/jnibridgeservicefactory.java.tpl +++ b/templates/jnibridge/service/jnibridgeservicefactory.java.tpl @@ -43,7 +43,7 @@ public class {{Camel .Interface.Name}}JniServiceFactory extends HandlerThread im { synchronized (this) { - Log.w("UE", "LIFECYCLE: {{Camel .Interface.Name}}JniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); + Log.i("UE", "LIFECYCLE: {{Camel .Interface.Name}}JniServiceFactory::onDestroy() - stop instance thread, service = " + jniService); Singleton.INSTANCE.quit(); } } @@ -72,7 +72,7 @@ public class {{Camel .Interface.Name}}JniServiceFactory extends HandlerThread im @NonNull private static {{Camel .Interface.Name}}JniServiceFactory createInstance() { - Log.w("UE", "LIFECYCLE: EngineFactory::createInstance()"); + Log.i("UE", "LIFECYCLE: EngineFactory::createInstance()"); {{Camel .Interface.Name}}JniServiceFactory t = new {{Camel .Interface.Name}}JniServiceFactory(); t.start(); diff --git a/templates/jnibridge/service/jnibridgeservicestarter.java.tpl b/templates/jnibridge/service/jnibridgeservicestarter.java.tpl index 093d4ab..f917881 100644 --- a/templates/jnibridge/service/jnibridgeservicestarter.java.tpl +++ b/templates/jnibridge/service/jnibridgeservicestarter.java.tpl @@ -21,11 +21,11 @@ public class {{Camel .Interface.Name }}JniServiceStarter { public static I{{Camel .Interface.Name }} start(Context context) { stop(context); androidService = new Intent(context, {{Camel .Interface.Name }}ServiceAdapter.class); - Log.w(TAG, "starter: created intent"); + Log.i(TAG, "starter: created intent"); context.startService(androidService); - Log.w(TAG, "starter: started intent (service) "); + Log.i(TAG, "starter: started intent (service) "); {{Camel .Interface.Name}}JniServiceFactory factory = {{Camel .Interface.Name}}JniServiceFactory.get(); - Log.w(TAG, "starter: factory set for {{Camel .Interface.Name}}JniServiceFactory"); + Log.i(TAG, "starter: factory set for {{Camel .Interface.Name}}JniServiceFactory"); return {{Camel .Interface.Name }}ServiceAdapter.setService(factory); } @@ -33,7 +33,7 @@ public class {{Camel .Interface.Name }}JniServiceStarter { { if (androidService != null) { - Log.w(TAG, "starter: stop the service"); + Log.i(TAG, "starter: stop the service"); context.stopService(androidService); } androidService = null; diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index 1b5d6aa..9d2623f 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -121,7 +121,7 @@ public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface @Override public {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}) { - Log.w(TAG, "request method {{camel .Name}} called, returnig default"); + Log.i(TAG, "request method {{camel .Name}} called, returnig default"); return {{ if not .Return.IsVoid }}{{ javaDefault "" .Return }}{{end }}; } diff --git a/templates/testclientapp/application.java.tpl b/templates/testclientapp/application.java.tpl index af12fac..c3c8516 100644 --- a/templates/testclientapp/application.java.tpl +++ b/templates/testclientapp/application.java.tpl @@ -145,7 +145,7 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ b{{Camel .Name}}.setText("{{.Name}}"); b{{Camel .Name}}.setOnClickListener(v -> { - Log.w(TAG, "CALLING METHOD {{.Name}} "); + Log.i(TAG, "CALLING METHOD {{.Name}} "); {{- range .Params}} {{javaType "" . }} {{javaVar .}} = {{javaTestValue "" .}}; {{- end}} @@ -254,12 +254,12 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ private void initServiceConnection( String servicePackage) { lastServicePackage = servicePackage; - Log.w(TAG, "init service connection the client "); + Log.i(TAG, "init service connection the client "); if (mClient == null) { mClient = new {{Camel $Interface.Name }}Client(this.getApplicationContext(), ""); - Log.w(TAG, "client created "); + Log.i(TAG, "client created "); mClient.addEventListener(this); } @@ -273,7 +273,7 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) { outputTextViewProp.setText("Property from service: {{.Name}} " + newValue); - Log.w(TAG, "Property from service: {{.Name}} " + newValue); + Log.i(TAG, "Property from service: {{.Name}} " + newValue); } {{- end }} {{- range $Interface.Signals }} @@ -282,7 +282,7 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ { String text = "Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } {{- end }} @Override @@ -290,11 +290,11 @@ public class {{Camel .Module.Name}}TestClientApp extends Activity implements I{{ { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } diff --git a/templates/testserviceapp/application.java.tpl b/templates/testserviceapp/application.java.tpl index 0673265..b993e29 100644 --- a/templates/testserviceapp/application.java.tpl +++ b/templates/testserviceapp/application.java.tpl @@ -148,7 +148,7 @@ public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{ b{{Camel .Name}}.setText("{{.Name}}"); b{{Camel .Name}}.setOnClickListener(v -> { - Log.w(TAG, "broadcasting singal {{.Name}} "); + Log.i(TAG, "broadcasting singal {{.Name}} "); {{- range .Params}} {{javaType "" . }} {{javaVar .}} = {{javaTestValue "" .}}; {{- end}} @@ -216,7 +216,7 @@ public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{ private void startMyService(){ stub_service = new Intent(this, {{Camel $Interface.Name }}ServiceAdapter.class); this.startService(stub_service); - Log.w(TAG, "Service started with stub backend"); + Log.i(TAG, "Service started with stub backend"); mBackend = {{Camel $Interface.Name }}ServiceAdapter.setService({{Camel $Interface.Name }}ServiceFactory.get()); mBackend.addEventListener(this); } @@ -244,7 +244,7 @@ public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{ public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) { outputTextViewProp.setText("Property from service: {{.Name}} " + newValue); - Log.w(TAG, "Property from service: {{.Name}} " + newValue); + Log.i(TAG, "Property from service: {{.Name}} " + newValue); } {{- end }} {{- range $Interface.Signals }} @@ -253,7 +253,7 @@ public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{ { String text = "Signal {{.Name}} "{{- range .Params -}} + " " + {{javaVar .}}{{ end}}; outputTextViewSig.setText(text); - Log.w(TAG, text); + Log.i(TAG, text); } {{- end }} @Override @@ -261,14 +261,13 @@ public class {{Camel .Module.Name}}TestServiceApp extends Activity implements I{ { if (isReady) { - Log.w(TAG, "Connected to service "); + Log.i(TAG, "Connected to service "); } else { - Log.w(TAG, "Disconnected from service "); + Log.i(TAG, "Disconnected from service "); } } - } {{- end}} From 38c775214b644953dc345e092e73e07fc082aeb9 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:11:13 +0100 Subject: [PATCH 43/45] fix: fix string comparision --- goldenmaster/counter/counterjniclient/CounterJniClient.java | 2 +- goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java | 2 +- .../tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java | 2 +- goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java | 2 +- .../tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java | 2 +- .../tbRefIfacesjniclient/SimpleLocalIfJniClient.java | 2 +- .../tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java | 2 +- .../tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java | 2 +- .../tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java | 2 +- .../tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java | 2 +- .../tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java | 2 +- .../tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java | 2 +- .../tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java | 2 +- .../tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java | 2 +- .../tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java | 2 +- .../tbSimplejniclient/NoOperationsInterfaceJniClient.java | 2 +- .../tbSimplejniclient/NoPropertiesInterfaceJniClient.java | 2 +- .../tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java | 2 +- .../tbSimplejniclient/SimpleArrayInterfaceJniClient.java | 2 +- .../tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java | 2 +- .../tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java | 2 +- .../testbed1jniclient/StructArray2InterfaceJniClient.java | 2 +- .../testbed1jniclient/StructArrayInterfaceJniClient.java | 2 +- .../testbed1/testbed1jniclient/StructInterfaceJniClient.java | 2 +- .../testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java | 2 +- .../testbed2jniclient/NestedStruct1InterfaceJniClient.java | 2 +- .../testbed2jniclient/NestedStruct2InterfaceJniClient.java | 2 +- .../testbed2jniclient/NestedStruct3InterfaceJniClient.java | 2 +- templates/jnibridge/client/jnibridgeclient.java.tpl | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/goldenmaster/counter/counterjniclient/CounterJniClient.java b/goldenmaster/counter/counterjniclient/CounterJniClient.java index 0205bd6..79b212d 100644 --- a/goldenmaster/counter/counterjniclient/CounterJniClient.java +++ b/goldenmaster/counter/counterjniclient/CounterJniClient.java @@ -175,7 +175,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java index 6583e00..4bd8cd0 100644 --- a/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java +++ b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java @@ -183,7 +183,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java index 5df307e..0ff6e7d 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniclient/EmptyIfJniClient.java @@ -51,7 +51,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java index 7c6cdc9..7d5ce16 100644 --- a/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java +++ b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java @@ -141,7 +141,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java index 98f17e2..76b09ca 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/ParentIfJniClient.java @@ -177,7 +177,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java index 5aa30f8..5aa7f14 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniclient/SimpleLocalIfJniClient.java @@ -82,7 +82,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java index adcedf9..15a1824 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java @@ -84,7 +84,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java index 58da0dc..2c64f25 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java @@ -117,7 +117,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java index 78879e9..ef8e7fe 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java @@ -84,7 +84,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java index 1502cf5..35fcafa 100644 --- a/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java @@ -117,7 +117,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java index e00cc4e..eee2fe2 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java @@ -84,7 +84,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java index da46e9f..88be680 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java @@ -117,7 +117,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java index ab90613..a9dff37 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java @@ -84,7 +84,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java index 89efdfe..e2e4e1a 100644 --- a/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java @@ -117,7 +117,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java index d565c93..cce3bd7 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java @@ -51,7 +51,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java index 18ef3a2..bb361af 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java @@ -77,7 +77,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java index 01c8be3..e60b6bd 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java @@ -87,7 +87,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java index 1253779..0f60c76 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java @@ -113,7 +113,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java index 25ee331..744235f 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java @@ -312,7 +312,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java index b2ddc7a..a7f5772 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java @@ -335,7 +335,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java index 57a8b5f..8a12387 100644 --- a/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java +++ b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java @@ -69,7 +69,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java index ef5b36a..6eea436 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java @@ -226,7 +226,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java index 5036b9b..068d6d4 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java @@ -216,7 +216,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java index f2ea2e0..b224af9 100644 --- a/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java +++ b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java @@ -183,7 +183,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java index d4454b2..8f710e8 100644 --- a/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java @@ -175,7 +175,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java index 0122e1b..de790ab 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -120,7 +120,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java index 14e3874..5e14020 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java @@ -117,7 +117,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java index 7d126c4..f87fa1c 100644 --- a/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java @@ -150,7 +150,7 @@ private boolean initServiceConnection(Context ctx, String servicePackage, String Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; diff --git a/templates/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl index e905c77..6a30ece 100644 --- a/templates/jnibridge/client/jnibridgeclient.java.tpl +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -143,7 +143,7 @@ public class {{Camel .Interface.Name}}JniClient extends Abstract{{Camel .Interfa Log.i(TAG, "client created "); mMessengerClient.addEventListener(this); } - if (lastServicePackage != servicePackage && mMessengerClient.isBoundToService()) { + if (!lastServicePackage.equals(servicePackage) && mMessengerClient.isBoundToService()) { unbind(); } lastServicePackage = servicePackage; From e0cfff3ffa517ae10145781b58efa7ea766e4865 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:12:27 +0100 Subject: [PATCH 44/45] fix: remove unused reference which may cause mem leak --- .../counter_android_service/CounterServiceAdapter.java | 6 ++---- .../tbEnum_android_service/EnumInterfaceServiceAdapter.java | 6 ++---- .../EmptyIfServiceAdapter.java | 6 ++---- .../tbNames_android_service/NamEsServiceAdapter.java | 6 ++---- .../tbRefIfaces_android_service/ParentIfServiceAdapter.java | 6 ++---- .../SimpleLocalIfServiceAdapter.java | 6 ++---- .../SameEnum1InterfaceServiceAdapter.java | 6 ++---- .../SameEnum2InterfaceServiceAdapter.java | 6 ++---- .../SameStruct1InterfaceServiceAdapter.java | 6 ++---- .../SameStruct2InterfaceServiceAdapter.java | 6 ++---- .../SameEnum1InterfaceServiceAdapter.java | 6 ++---- .../SameEnum2InterfaceServiceAdapter.java | 6 ++---- .../SameStruct1InterfaceServiceAdapter.java | 6 ++---- .../SameStruct2InterfaceServiceAdapter.java | 6 ++---- .../EmptyInterfaceServiceAdapter.java | 6 ++---- .../NoOperationsInterfaceServiceAdapter.java | 6 ++---- .../NoPropertiesInterfaceServiceAdapter.java | 6 ++---- .../NoSignalsInterfaceServiceAdapter.java | 6 ++---- .../SimpleArrayInterfaceServiceAdapter.java | 6 ++---- .../SimpleInterfaceServiceAdapter.java | 6 ++---- .../VoidInterfaceServiceAdapter.java | 6 ++---- .../StructArray2InterfaceServiceAdapter.java | 6 ++---- .../StructArrayInterfaceServiceAdapter.java | 6 ++---- .../StructInterfaceServiceAdapter.java | 6 ++---- .../ManyParamInterfaceServiceAdapter.java | 6 ++---- .../NestedStruct1InterfaceServiceAdapter.java | 6 ++---- .../NestedStruct2InterfaceServiceAdapter.java | 6 ++---- .../NestedStruct3InterfaceServiceAdapter.java | 6 ++---- templates/android/service/serviceadapter.java.tpl | 6 ++---- 29 files changed, 58 insertions(+), 116 deletions(-) diff --git a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java index a620a09..da42100 100644 --- a/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ICounterEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java index edcfcfc..f9e406f 100644 --- a/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java @@ -89,7 +89,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -161,13 +161,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IEnumInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java index 9755db5..810548b 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IEmptyIfEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java index b2bafaf..a98d63f 100644 --- a/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INamEsEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java index 1a84fbc..84be2e9 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IParentIfEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java index bf484f7..8d197ad 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISimpleLocalIfEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java index 0eefc97..f371dda 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameEnum1InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java index df1c137..c1ed470 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java @@ -85,7 +85,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -157,13 +157,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameEnum2InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java index aadebe5..406b2d4 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameStruct1InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java index 63454c3..e6dcbcd 100644 --- a/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java @@ -85,7 +85,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -157,13 +157,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameStruct2InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java index 68e9bdf..58762b1 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameEnum1InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java index 7d1d50f..5fa02bc 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java @@ -85,7 +85,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -157,13 +157,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameEnum2InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java index b9a9b78..4e31854 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameStruct1InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java index 120eff6..97919fa 100644 --- a/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java @@ -85,7 +85,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -157,13 +157,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISameStruct2InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java index 00eb14e..3734dc4 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IEmptyInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java index 0964139..04c9c1c 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INoOperationsInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java index aa49592..1aeb4ad 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INoPropertiesInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java index 0a3ff1b..61bfab9 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INoSignalsInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java index e70eefe..c6dc78b 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISimpleArrayInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java index 1541394..7420f58 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements ISimpleInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java index 73e1cb2..d1d0831 100644 --- a/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IVoidInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java index 9f4ed49..c92d80c 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java @@ -101,7 +101,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -173,13 +173,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IStructArray2InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java index 8d56507..253e540 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java @@ -91,7 +91,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -163,13 +163,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IStructArrayInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java index ccd4507..e9cc82e 100644 --- a/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java @@ -89,7 +89,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -161,13 +161,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IStructInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java index 40cc5ae..5db55f7 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java @@ -81,7 +81,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -153,13 +153,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements IManyParamInterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java index 2767739..f0b855f 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java @@ -83,7 +83,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -155,13 +155,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INestedStruct1InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java index 5befe8c..4b62c08 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java @@ -85,7 +85,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -157,13 +157,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INestedStruct2InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java index 7ff0f3f..5a97a71 100644 --- a/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java @@ -87,7 +87,7 @@ public void onCreate() { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -159,13 +159,11 @@ private static String Name(Context context) */ class IncomingHandler extends Handler implements INestedStruct3InterfaceEventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) diff --git a/templates/android/service/serviceadapter.java.tpl b/templates/android/service/serviceadapter.java.tpl index aa52c37..554e1b2 100644 --- a/templates/android/service/serviceadapter.java.tpl +++ b/templates/android/service/serviceadapter.java.tpl @@ -137,7 +137,7 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service { mHandler.removeCallbacksAndMessages(null); } - mHandler = new IncomingHandler(this); + mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); if (mBackendService != null) { @@ -209,13 +209,11 @@ public class {{Camel .Interface.Name }}ServiceAdapter extends Service */ class IncomingHandler extends Handler implements I{{Camel .Interface.Name }}EventListener { - private final Service mApplicationContext; private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); - IncomingHandler(Service context) + IncomingHandler() { super(Looper.getMainLooper()); - mApplicationContext = context; } private void sendMessageToClients(Message msg) From b8cf43d42dba84645a099911a68762739c06d310 Mon Sep 17 00:00:00 2001 From: dorotaphanSiili <108282075+dorotaphanSiili@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:14:17 +0100 Subject: [PATCH 45/45] chores: make intention explicit on used threads --- .../src/main/java/counter/counter_impl/CounterService.java | 2 +- goldenmaster/counter/counterjniservice/CounterJniService.java | 2 +- .../src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java | 2 +- .../tbEnum/tbEnumjniservice/EnumInterfaceJniService.java | 2 +- .../java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java | 2 +- .../tbIfaceimportjniservice/EmptyIfJniService.java | 2 +- .../src/main/java/tbNames/tbNames_impl/NamEsService.java | 2 +- goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java | 2 +- .../main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java | 2 +- .../java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java | 2 +- .../tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java | 2 +- .../tbRefIfacesjniservice/SimpleLocalIfJniService.java | 2 +- .../java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java | 2 +- .../java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java | 2 +- .../java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java | 2 +- .../java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java | 2 +- .../tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java | 2 +- .../tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java | 2 +- .../tbSame1jniservice/SameStruct1InterfaceJniService.java | 2 +- .../tbSame1jniservice/SameStruct2InterfaceJniService.java | 2 +- .../java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java | 2 +- .../java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java | 2 +- .../java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java | 2 +- .../java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java | 2 +- .../tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java | 2 +- .../tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java | 2 +- .../tbSame2jniservice/SameStruct1InterfaceJniService.java | 2 +- .../tbSame2jniservice/SameStruct2InterfaceJniService.java | 2 +- .../main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java | 2 +- .../tbSimple/tbSimple_impl/NoOperationsInterfaceService.java | 2 +- .../tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java | 2 +- .../java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java | 2 +- .../tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java | 2 +- .../java/tbSimple/tbSimple_impl/SimpleInterfaceService.java | 2 +- .../main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java | 2 +- .../tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java | 2 +- .../tbSimplejniservice/NoOperationsInterfaceJniService.java | 2 +- .../tbSimplejniservice/NoPropertiesInterfaceJniService.java | 2 +- .../tbSimplejniservice/NoSignalsInterfaceJniService.java | 2 +- .../tbSimplejniservice/SimpleArrayInterfaceJniService.java | 2 +- .../tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java | 2 +- .../tbSimple/tbSimplejniservice/VoidInterfaceJniService.java | 2 +- .../testbed1/testbed1_impl/StructArray2InterfaceService.java | 2 +- .../testbed1/testbed1_impl/StructArrayInterfaceService.java | 2 +- .../java/testbed1/testbed1_impl/StructInterfaceService.java | 2 +- .../testbed1jniservice/StructArray2InterfaceJniService.java | 2 +- .../testbed1jniservice/StructArrayInterfaceJniService.java | 2 +- .../testbed1/testbed1jniservice/StructInterfaceJniService.java | 2 +- .../java/testbed2/testbed2_impl/ManyParamInterfaceService.java | 2 +- .../testbed2/testbed2_impl/NestedStruct1InterfaceService.java | 2 +- .../testbed2/testbed2_impl/NestedStruct2InterfaceService.java | 2 +- .../testbed2/testbed2_impl/NestedStruct3InterfaceService.java | 2 +- .../testbed2jniservice/ManyParamInterfaceJniService.java | 2 +- .../testbed2jniservice/NestedStruct1InterfaceJniService.java | 2 +- .../testbed2jniservice/NestedStruct2InterfaceJniService.java | 2 +- .../testbed2jniservice/NestedStruct3InterfaceJniService.java | 2 +- templates/jnibridge/service/jnibridgeservice.java.tpl | 2 +- templates/stub/implservice.java.tpl | 2 +- 58 files changed, 58 insertions(+), 58 deletions(-) diff --git a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java index e4fd450..c1f4d90 100644 --- a/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java +++ b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java @@ -23,7 +23,7 @@ public class CounterService extends AbstractCounter { private final static String TAG = "CounterService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private customTypes.customTypes_api.Vector3D m_vector = new customTypes.customTypes_api.Vector3D(); private org.apache.commons.math3.geometry.euclidean.threed.Vector3D m_extern_vector = new org.apache.commons.math3.geometry.euclidean.threed.Vector3D(0.0, 0.0, 0.0); private customTypes.customTypes_api.Vector3D[] m_vectorArray = new customTypes.customTypes_api.Vector3D[]{}; diff --git a/goldenmaster/counter/counterjniservice/CounterJniService.java b/goldenmaster/counter/counterjniservice/CounterJniService.java index 7af6e1d..fc52465 100644 --- a/goldenmaster/counter/counterjniservice/CounterJniService.java +++ b/goldenmaster/counter/counterjniservice/CounterJniService.java @@ -22,7 +22,7 @@ public class CounterJniService extends AbstractCounter { private final static String TAG = "CounterJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public CounterJniService() { diff --git a/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java b/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java index ca2ebe8..41b61d9 100644 --- a/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java +++ b/goldenmaster/tbEnum/tbEnum_impl/src/main/java/tbEnum/tbEnum_impl/EnumInterfaceService.java @@ -27,7 +27,7 @@ public class EnumInterfaceService extends AbstractEnumInterface { private final static String TAG = "EnumInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Enum0 m_prop0 = Enum0.Value0; private Enum1 m_prop1 = Enum1.Value1; private Enum2 m_prop2 = Enum2.Value2; diff --git a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java index e9bfa39..356b7f3 100644 --- a/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java @@ -30,7 +30,7 @@ public class EnumInterfaceJniService extends AbstractEnumInterface { private final static String TAG = "EnumInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public EnumInterfaceJniService() { diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java index 009b1b8..4d836b6 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_impl/src/main/java/tbIfaceimport/tbIfaceimport_impl/EmptyIfService.java @@ -23,7 +23,7 @@ public class EmptyIfService extends AbstractEmptyIf { private final static String TAG = "EmptyIfService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public EmptyIfService() { diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java index 8bd66ea..2939e8a 100644 --- a/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java +++ b/goldenmaster/tbIfaceimport/tbIfaceimportjniservice/EmptyIfJniService.java @@ -22,7 +22,7 @@ public class EmptyIfJniService extends AbstractEmptyIf { private final static String TAG = "EmptyIfJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public EmptyIfJniService() { diff --git a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java index 77eaf18..389bc1a 100644 --- a/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java +++ b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java @@ -24,7 +24,7 @@ public class NamEsService extends AbstractNamEs { private final static String TAG = "NamEsService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private boolean m_Switch = false; private int m_SOME_PROPERTY = 0; private int m_Some_Poperty2 = 0; diff --git a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java index 168f990..4ff2c3b 100644 --- a/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java @@ -24,7 +24,7 @@ public class NamEsJniService extends AbstractNamEs { private final static String TAG = "NamEsJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NamEsJniService() { diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java index 8a08792..1aa85f3 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/ParentIfService.java @@ -25,7 +25,7 @@ public class ParentIfService extends AbstractParentIf { private final static String TAG = "ParentIfService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private ISimpleLocalIf m_localIf = null; private ISimpleLocalIf[] m_localIfList = new ISimpleLocalIf[]{}; private tbIfaceimport.tbIfaceimport_api.IEmptyIf m_importedIf = null; diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java index f24b04f..7579653 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_impl/src/main/java/tbRefIfaces/tbRefIfaces_impl/SimpleLocalIfService.java @@ -23,7 +23,7 @@ public class SimpleLocalIfService extends AbstractSimpleLocalIf { private final static String TAG = "SimpleLocalIfService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private int m_intProperty = 0; public SimpleLocalIfService() diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java index 832c5af..fbe142f 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/ParentIfJniService.java @@ -24,7 +24,7 @@ public class ParentIfJniService extends AbstractParentIf { private final static String TAG = "ParentIfJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public ParentIfJniService() { diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java index 0df6552..5e6f523 100644 --- a/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java +++ b/goldenmaster/tbRefIfaces/tbRefIfacesjniservice/SimpleLocalIfJniService.java @@ -22,7 +22,7 @@ public class SimpleLocalIfJniService extends AbstractSimpleLocalIf { private final static String TAG = "SimpleLocalIfJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SimpleLocalIfJniService() { diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java index 19a05ec..538f4fe 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java @@ -24,7 +24,7 @@ public class SameEnum1InterfaceService extends AbstractSameEnum1Interface { private final static String TAG = "SameEnum1InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Enum1 m_prop1 = Enum1.Value1; public SameEnum1InterfaceService() diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java index 29eefe4..faf32ff 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java @@ -25,7 +25,7 @@ public class SameEnum2InterfaceService extends AbstractSameEnum2Interface { private final static String TAG = "SameEnum2InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Enum1 m_prop1 = Enum1.Value1; private Enum2 m_prop2 = Enum2.Value1; diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java index 5c1a262..cd0e153 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java @@ -24,7 +24,7 @@ public class SameStruct1InterfaceService extends AbstractSameStruct1Interface { private final static String TAG = "SameStruct1InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Struct1 m_prop1 = new Struct1(); public SameStruct1InterfaceService() diff --git a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java index 413cb27..9fecfb6 100644 --- a/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct2InterfaceService.java @@ -25,7 +25,7 @@ public class SameStruct2InterfaceService extends AbstractSameStruct2Interface { private final static String TAG = "SameStruct2InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Struct2 m_prop1 = new Struct2(); private Struct2 m_prop2 = new Struct2(); diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java index 009de98..58a2878 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java @@ -24,7 +24,7 @@ public class SameEnum1InterfaceJniService extends AbstractSameEnum1Interface { private final static String TAG = "SameEnum1InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameEnum1InterfaceJniService() { diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java index ef7cafc..f75bc33 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java @@ -26,7 +26,7 @@ public class SameEnum2InterfaceJniService extends AbstractSameEnum2Interface { private final static String TAG = "SameEnum2InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameEnum2InterfaceJniService() { diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java index ffecb70..c4cbb41 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java @@ -24,7 +24,7 @@ public class SameStruct1InterfaceJniService extends AbstractSameStruct1Interface private final static String TAG = "SameStruct1InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameStruct1InterfaceJniService() { diff --git a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java index 74db547..44aae5d 100644 --- a/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java @@ -26,7 +26,7 @@ public class SameStruct2InterfaceJniService extends AbstractSameStruct2Interface private final static String TAG = "SameStruct2InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameStruct2InterfaceJniService() { diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java index f01029e..44c8992 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java @@ -24,7 +24,7 @@ public class SameEnum1InterfaceService extends AbstractSameEnum1Interface { private final static String TAG = "SameEnum1InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Enum1 m_prop1 = Enum1.Value1; public SameEnum1InterfaceService() diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java index edbbb31..40c9576 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java @@ -25,7 +25,7 @@ public class SameEnum2InterfaceService extends AbstractSameEnum2Interface { private final static String TAG = "SameEnum2InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Enum1 m_prop1 = Enum1.Value1; private Enum2 m_prop2 = Enum2.Value1; diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java index c99dacc..4327e76 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java @@ -24,7 +24,7 @@ public class SameStruct1InterfaceService extends AbstractSameStruct1Interface { private final static String TAG = "SameStruct1InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Struct1 m_prop1 = new Struct1(); public SameStruct1InterfaceService() diff --git a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java index d07040e..db1b3a5 100644 --- a/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct2InterfaceService.java @@ -25,7 +25,7 @@ public class SameStruct2InterfaceService extends AbstractSameStruct2Interface { private final static String TAG = "SameStruct2InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private Struct2 m_prop1 = new Struct2(); private Struct2 m_prop2 = new Struct2(); diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java index 7de9556..44c04a6 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java @@ -24,7 +24,7 @@ public class SameEnum1InterfaceJniService extends AbstractSameEnum1Interface { private final static String TAG = "SameEnum1InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameEnum1InterfaceJniService() { diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java index f3d5c4d..cc67216 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java @@ -26,7 +26,7 @@ public class SameEnum2InterfaceJniService extends AbstractSameEnum2Interface { private final static String TAG = "SameEnum2InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameEnum2InterfaceJniService() { diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java index fa1417f..eba8102 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java @@ -24,7 +24,7 @@ public class SameStruct1InterfaceJniService extends AbstractSameStruct1Interface private final static String TAG = "SameStruct1InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameStruct1InterfaceJniService() { diff --git a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java index 1cea6e5..f3434d3 100644 --- a/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java @@ -26,7 +26,7 @@ public class SameStruct2InterfaceJniService extends AbstractSameStruct2Interface private final static String TAG = "SameStruct2InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SameStruct2InterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java index 3758894..a714dfd 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/EmptyInterfaceService.java @@ -23,7 +23,7 @@ public class EmptyInterfaceService extends AbstractEmptyInterface { private final static String TAG = "EmptyInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public EmptyInterfaceService() { diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java index 39337ea..eb9368b 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoOperationsInterfaceService.java @@ -23,7 +23,7 @@ public class NoOperationsInterfaceService extends AbstractNoOperationsInterface private final static String TAG = "NoOperationsInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private boolean m_propBool = false; private int m_propInt = 0; diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java index ed08fa1..7bf86bf 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoPropertiesInterfaceService.java @@ -23,7 +23,7 @@ public class NoPropertiesInterfaceService extends AbstractNoPropertiesInterface private final static String TAG = "NoPropertiesInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NoPropertiesInterfaceService() { diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java index e276b58..f0fb250 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/NoSignalsInterfaceService.java @@ -23,7 +23,7 @@ public class NoSignalsInterfaceService extends AbstractNoSignalsInterface { private final static String TAG = "NoSignalsInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private boolean m_propBool = false; private int m_propInt = 0; diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java index a4e864e..dd5f2f4 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleArrayInterfaceService.java @@ -23,7 +23,7 @@ public class SimpleArrayInterfaceService extends AbstractSimpleArrayInterface { private final static String TAG = "SimpleArrayInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private boolean[] m_propBool = new boolean[]{}; private int[] m_propInt = new int[]{}; private int[] m_propInt32 = new int[]{}; diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java index 7608d36..b35617f 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java @@ -23,7 +23,7 @@ public class SimpleInterfaceService extends AbstractSimpleInterface { private final static String TAG = "SimpleInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private boolean m_propBool = false; private int m_propInt = 0; private int m_propInt32 = 0; diff --git a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java index 9fc0c9c..982e58b 100644 --- a/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/VoidInterfaceService.java @@ -23,7 +23,7 @@ public class VoidInterfaceService extends AbstractVoidInterface { private final static String TAG = "VoidInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public VoidInterfaceService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java index a4ca654..247a5de 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java @@ -22,7 +22,7 @@ public class EmptyInterfaceJniService extends AbstractEmptyInterface { private final static String TAG = "EmptyInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public EmptyInterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java index ac36257..a88f436 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java @@ -22,7 +22,7 @@ public class NoOperationsInterfaceJniService extends AbstractNoOperationsInterfa private final static String TAG = "NoOperationsInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NoOperationsInterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java index 308761c..97cfe46 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java @@ -22,7 +22,7 @@ public class NoPropertiesInterfaceJniService extends AbstractNoPropertiesInterfa private final static String TAG = "NoPropertiesInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NoPropertiesInterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java index 11a652d..e77cf05 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java @@ -22,7 +22,7 @@ public class NoSignalsInterfaceJniService extends AbstractNoSignalsInterface { private final static String TAG = "NoSignalsInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NoSignalsInterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java index 2a9a792..3dde9e5 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java @@ -22,7 +22,7 @@ public class SimpleArrayInterfaceJniService extends AbstractSimpleArrayInterface private final static String TAG = "SimpleArrayInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SimpleArrayInterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java index 40f756f..86731be 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java @@ -22,7 +22,7 @@ public class SimpleInterfaceJniService extends AbstractSimpleInterface { private final static String TAG = "SimpleInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public SimpleInterfaceJniService() { diff --git a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java index 087cede..4382294 100644 --- a/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java @@ -22,7 +22,7 @@ public class VoidInterfaceJniService extends AbstractVoidInterface { private final static String TAG = "VoidInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public VoidInterfaceJniService() { diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java index 407f4d5..95c7099 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java @@ -33,7 +33,7 @@ public class StructArray2InterfaceService extends AbstractStructArray2Interface private final static String TAG = "StructArray2InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private StructBoolWithArray m_propBool = new StructBoolWithArray(); private StructIntWithArray m_propInt = new StructIntWithArray(); private StructFloatWithArray m_propFloat = new StructFloatWithArray(); diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java index 1a6c473..c003d7f 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java @@ -28,7 +28,7 @@ public class StructArrayInterfaceService extends AbstractStructArrayInterface { private final static String TAG = "StructArrayInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private StructBool[] m_propBool = new StructBool[]{}; private StructInt[] m_propInt = new StructInt[]{}; private StructFloat[] m_propFloat = new StructFloat[]{}; diff --git a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java index 306315e..825b52d 100644 --- a/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java @@ -27,7 +27,7 @@ public class StructInterfaceService extends AbstractStructInterface { private final static String TAG = "StructInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private StructBool m_propBool = new StructBool(); private StructInt m_propInt = new StructInt(); private StructFloat m_propFloat = new StructFloat(); diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java index f25ea9a..ccebe54 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java @@ -42,7 +42,7 @@ public class StructArray2InterfaceJniService extends AbstractStructArray2Interfa private final static String TAG = "StructArray2InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public StructArray2InterfaceJniService() { diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java index 37083ef..a9c1dab 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java @@ -32,7 +32,7 @@ public class StructArrayInterfaceJniService extends AbstractStructArrayInterface private final static String TAG = "StructArrayInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public StructArrayInterfaceJniService() { diff --git a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java index 91f4831..1e57b42 100644 --- a/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java @@ -30,7 +30,7 @@ public class StructInterfaceJniService extends AbstractStructInterface { private final static String TAG = "StructInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public StructInterfaceJniService() { diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java index 61d522f..3d2ef78 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java @@ -23,7 +23,7 @@ public class ManyParamInterfaceService extends AbstractManyParamInterface { private final static String TAG = "ManyParamInterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private int m_prop1 = 0; private int m_prop2 = 0; private int m_prop3 = 0; diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java index ff5ba00..7c1b7db 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java @@ -24,7 +24,7 @@ public class NestedStruct1InterfaceService extends AbstractNestedStruct1Interfac private final static String TAG = "NestedStruct1InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private NestedStruct1 m_prop1 = new NestedStruct1(); public NestedStruct1InterfaceService() diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java index 2d6c1eb..0947c36 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java @@ -25,7 +25,7 @@ public class NestedStruct2InterfaceService extends AbstractNestedStruct2Interfac private final static String TAG = "NestedStruct2InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private NestedStruct1 m_prop1 = new NestedStruct1(); private NestedStruct2 m_prop2 = new NestedStruct2(); diff --git a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java index d3ddca9..2b7ad1b 100644 --- a/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java @@ -26,7 +26,7 @@ public class NestedStruct3InterfaceService extends AbstractNestedStruct3Interfac private final static String TAG = "NestedStruct3InterfaceService"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private NestedStruct1 m_prop1 = new NestedStruct1(); private NestedStruct2 m_prop2 = new NestedStruct2(); private NestedStruct3 m_prop3 = new NestedStruct3(); diff --git a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java index 1919f7e..14d6fbc 100644 --- a/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java @@ -22,7 +22,7 @@ public class ManyParamInterfaceJniService extends AbstractManyParamInterface { private final static String TAG = "ManyParamInterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public ManyParamInterfaceJniService() { diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java index addea51..fc84bbd 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java @@ -24,7 +24,7 @@ public class NestedStruct1InterfaceJniService extends AbstractNestedStruct1Inter private final static String TAG = "NestedStruct1InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NestedStruct1InterfaceJniService() { diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java index f0f1710..8a17697 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java @@ -26,7 +26,7 @@ public class NestedStruct2InterfaceJniService extends AbstractNestedStruct2Inter private final static String TAG = "NestedStruct2InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NestedStruct2InterfaceJniService() { diff --git a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java index 8141da8..f8467aa 100644 --- a/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java @@ -28,7 +28,7 @@ public class NestedStruct3InterfaceJniService extends AbstractNestedStruct3Inter private final static String TAG = "NestedStruct3InterfaceJniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public NestedStruct3InterfaceJniService() { diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl index 839c879..6dcbe84 100644 --- a/templates/jnibridge/service/jnibridgeservice.java.tpl +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -77,7 +77,7 @@ public class {{Camel .Interface.Name}}JniService extends Abstract{{Camel .Interf private final static String TAG = "{{Camel .Interface.Name}}JniService"; private static boolean isServiceReady = false; - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public {{Camel .Interface.Name}}JniService() { diff --git a/templates/stub/implservice.java.tpl b/templates/stub/implservice.java.tpl index 9d2623f..d125027 100644 --- a/templates/stub/implservice.java.tpl +++ b/templates/stub/implservice.java.tpl @@ -77,7 +77,7 @@ public class {{Camel .Interface.Name}}Service extends Abstract{{Camel .Interface private final static String TAG = "{{Camel .Interface.Name}}Service"; private static boolean isServiceReady = true;//Use if you're waiting for some setup to be done - private static final ExecutorService executor = Executors.newFixedThreadPool(1); + private static final ExecutorService executor = Executors.newSingleThreadExecutor(); {{- range .Interface.Properties }} private {{javaReturn "" .}} m_{{javaVar .}} = {{ javaDefault "" . }};