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' diff --git a/.gitignore b/.gitignore index 675d0f7..27c8854 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ .DS_Store .vscode/ +.vs/ .idea/ +**/bin/ +**/.gradle/ +**/build/ test/ -/demo/app/bin -/demo/gradle 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 diff --git a/apigear/goldenmaster.solution.yaml b/apigear/goldenmaster.solution.yaml index cc0d4cd..c1292ba 100644 --- a/apigear/goldenmaster.solution.yaml +++ b/apigear/goldenmaster.solution.yaml @@ -14,6 +14,11 @@ targets: - ../test-apis/testbed.same2.module.yaml - ../test-apis/testbed.simple.module.yaml - ../test-apis/testbed.struct.module.yaml + - ../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 diff --git a/goldenmaster/build.gradle b/goldenmaster/build.gradle new file mode 100644 index 0000000..c1b914b --- /dev/null +++ b/goldenmaster/build.gradle @@ -0,0 +1,82 @@ +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 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') + +} + +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/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..6160bf5 --- /dev/null +++ b/goldenmaster/counter/counter_android_client/build.gradle @@ -0,0 +1,38 @@ +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' + 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 new file mode 100644 index 0000000..bfbfcbd --- /dev/null +++ b/goldenmaster/counter/counter_android_client/src/main/java/counter/counter_android_client/CounterClient.java @@ -0,0 +1,677 @@ +//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.i(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(); + 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(); + 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(); + 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(); + 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 != null && ! m_vector.equals(vector)) + || (m_vector == null && vector != null )) + { + 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 != null && ! m_vector.equals(vector)) + || (m_vector == null && vector != null )) + { + 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 != null && ! m_extern_vector.equals(extern_vector)) + || (m_extern_vector == null && extern_vector != null )) + { + 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 != null && ! m_extern_vector.equals(extern_vector)) + || (m_extern_vector == null && extern_vector != null )) + { + 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..096f8fa --- /dev/null +++ b/goldenmaster/counter/counter_android_client/src/test/java/counter/counter_android_client/CounterClientTest.java @@ -0,0 +1,523 @@ +//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.ICounter; +import counter.counter_android_messenger.CounterParcelable; +import counter.counter_impl.CounterService; + +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..da42100 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/main/java/counter/counter_android_service/CounterServiceAdapter.java @@ -0,0 +1,488 @@ +//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(); + 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 ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..0cd05b7 --- /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.i("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.i("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..60d1d15 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + CounterServiceFactory factory = CounterServiceFactory.get(); + Log.i(TAG, "starter: factory set for CounterServiceFactory"); + return CounterServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..86a9685 --- /dev/null +++ b/goldenmaster/counter/counter_android_service/src/test/java/counter/counter_android_service/CounterServiceAdapterTest.java @@ -0,0 +1,543 @@ +//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.ICounter; +import counter.counter_android_messenger.CounterParcelable; +import counter.counter_impl.CounterService; + + +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..f67333b --- /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.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( + 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.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( + 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.i(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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new CounterClient(this.getApplicationContext(), ""); + Log.i(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.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.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.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.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.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/counter/counter_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..c1f4d90 --- /dev/null +++ b/goldenmaster/counter/counter_impl/src/main/java/counter/counter_impl/CounterService.java @@ -0,0 +1,204 @@ +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.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[]{}; + 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 != null && ! m_vector.equals(vector)) + || (m_vector == null && vector != null )) + { + 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 != null && ! m_extern_vector.equals(extern_vector)) + || (m_extern_vector == null && extern_vector != null )) + { + 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.i(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.i(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.i(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.i(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..79b212d --- /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.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onVectorChanged(customTypes.customTypes_api.Vector3D 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.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnExternVectorChanged(newValue); + } + @Override + public void onVectorArrayChanged(customTypes.customTypes_api.Vector3D[] 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.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.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); + 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..fc52465 --- /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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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..6d0c176 --- /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.i("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.i("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..6eacc0c --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + CounterJniServiceFactory factory = CounterJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for CounterJniServiceFactory"); + return CounterServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..70c1ead --- /dev/null +++ b/goldenmaster/counter/counterserviceexample/src/main/java/counter/counterserviceexample/CounterTestServiceApp.java @@ -0,0 +1,255 @@ +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.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(); + 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.i(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.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.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.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.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.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/counter/counterserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/customTypes/customTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/customTypes/customTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/externTypes/externTypes_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/externTypes/externTypesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 new file mode 100644 index 0000000..f24fcf9 --- /dev/null +++ b/goldenmaster/goldenmaster_example/build.gradle @@ -0,0 +1,82 @@ +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") + 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/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..a29d82c --- /dev/null +++ b/goldenmaster/goldenmaster_example/src/main/java/goldenmaster_example/GoldenmasterMainActivity.java @@ -0,0 +1,200 @@ +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"); + 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); + 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..fa20611 --- /dev/null +++ b/goldenmaster/settings.gradle @@ -0,0 +1,44 @@ +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' +includeBuild 'customTypes' +includeBuild 'externTypes' +includeBuild 'counter' +includeBuild 'tbIfaceimport' +includeBuild 'tbRefIfaces' + +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..4b75571 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..8095c67 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + 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 new file mode 100644 index 0000000..7d21021 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/main/java/tbEnum/tbEnum_android_client/EnumInterfaceClient.java @@ -0,0 +1,718 @@ +//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.i(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()); + + + 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..809f3d7 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_client/src/test/java/tbEnum/tbEnum_android_client/EnumInterfaceClientTest.java @@ -0,0 +1,545 @@ +//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.IEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceParcelable; +import tbEnum.tbEnum_impl.EnumInterfaceService; + +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); + } + @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); + } + + @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); + } + + @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); + } + + @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_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 new file mode 100644 index 0000000..5a00c6c --- /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') + 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' + 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..b6426ec --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/build.gradle @@ -0,0 +1,36 @@ +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..f9e406f --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/main/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapter.java @@ -0,0 +1,519 @@ +//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 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<>(); + + public EnumInterfaceServiceAdapter() + { + } + + public static IEnumInterface setService(IEnumInterfaceServiceFactory 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(EnumInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(EnumInterfaceService) 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(); + 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: EnumInterfaceService::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(EnumInterfaceService) - 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 IEnumInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..649c810 --- /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.i("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.i("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..cedb0c4 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + EnumInterfaceServiceFactory factory = EnumInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for EnumInterfaceServiceFactory"); + return EnumInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..4eb8434 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_android_service/src/test/java/tbEnum/tbEnum_android_service/EnumInterfaceServiceAdapterTest.java @@ -0,0 +1,578 @@ +//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.IEnumInterface; +import tbEnum.tbEnum_android_messenger.EnumInterfaceParcelable; +import tbEnum.tbEnum_impl.EnumInterfaceService; + + +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()); + 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); + } + @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); + } + @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); + } + @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); + } + @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..90214bf --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/AbstractEnumInterface.java @@ -0,0 +1,84 @@ +package tbEnum.tbEnum_api; + +import tbEnum.tbEnum_api.IEnumInterfaceEventListener; +import tbEnum.tbEnum_api.IEnumInterface; +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..b66f915 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_api/src/main/java/tbEnum/tbEnum_api/TbEnumTestHelper.java @@ -0,0 +1,19 @@ +package tbEnum.tbEnum_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +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 new file mode 100644 index 0000000..0e8a889 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbEnum" +version = "1.0.0" + +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..fc571ef --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_client_example/src/main/java/tbEnum/tbEnum_client_example/TbEnumTestClientApp.java @@ -0,0 +1,355 @@ +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 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.i(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.i(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.i(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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new EnumInterfaceClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: prop0 " + newValue); + } + @Override + public void onProp1Changed(Enum1 newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: prop2 " + newValue); + } + @Override + public void onProp3Changed(Enum3 newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSig1(Enum1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSig2(Enum2 param2) + { + String text = "Signal sig2 "+ " " + param2; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSig3(Enum3 param3) + { + String text = "Signal sig3 "+ " " + param3; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnum_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..77b3727 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnum_impl/additions.gradle @@ -0,0 +1,19 @@ + +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..41b61d9 --- /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.newSingleThreadExecutor(); + 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.i(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.i(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.i(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.i(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..4bd8cd0 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumjniclient/EnumInterfaceJniClient.java @@ -0,0 +1,263 @@ +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_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; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new EnumInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp0Changed(Enum0 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp0Changed(newValue); + } + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onProp3Changed(Enum3 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp3Changed(newValue); + } + @Override + public void onSig0(Enum0 param0) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig0 "+ " " + param0); + nativeOnSig0(param0); + } + @Override + public void onSig1(Enum1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Enum2 param2) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig2 "+ " " + param2); + nativeOnSig2(param2); + } + @Override + public void onSig3(Enum3 param3) + { + Log.i(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..356b7f3 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumjniservice/EnumInterfaceJniService.java @@ -0,0 +1,224 @@ +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_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; +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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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..38b1d3d --- /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.i("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.i("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..790193e --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + EnumInterfaceJniServiceFactory factory = EnumInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for EnumInterfaceJniServiceFactory"); + return EnumInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..809487a --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbEnum" +version = "1.0.0" + + +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..49c4166 --- /dev/null +++ b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/java/tbEnum/tbEnumserviceexample/TbEnumTestServiceApp.java @@ -0,0 +1,311 @@ +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.i(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.i(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.i(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.i(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.i(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.i(TAG, "Property from service: prop0 " + newValue); + } + @Override + public void onProp1Changed(Enum1 newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: prop2 " + newValue); + } + @Override + public void onProp3Changed(Enum3 newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSig1(Enum1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSig2(Enum2 param2) + { + String text = "Signal sig2 "+ " " + param2; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSig3(Enum3 param3) + { + String text = "Signal sig3 "+ " " + param3; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbEnum/tbEnumserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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/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..4341235 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + testImplementation project(':tbIfaceimport_impl') +} 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..08f022a --- /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.i(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..57cc7ed --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_client/src/test/java/tbIfaceimport/tbIfaceimport_android_client/EmptyIfClientTest.java @@ -0,0 +1,149 @@ +//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.IEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable; +import tbIfaceimport.tbIfaceimport_impl.EmptyIfService; + +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..810548b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/main/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapter.java @@ -0,0 +1,251 @@ +//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(); + 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 ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..454fe4b --- /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.i("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.i("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..acf3a7e --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + EmptyIfServiceFactory factory = EmptyIfServiceFactory.get(); + Log.i(TAG, "starter: factory set for EmptyIfServiceFactory"); + return EmptyIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..ec1d279 --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimport_android_service/src/test/java/tbIfaceimport/tbIfaceimport_android_service/EmptyIfServiceAdapterTest.java @@ -0,0 +1,202 @@ +//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.IEmptyIf; +import tbIfaceimport.tbIfaceimport_android_messenger.EmptyIfParcelable; +import tbIfaceimport.tbIfaceimport_impl.EmptyIfService; + + +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..29dfc5a --- /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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new EmptyIfClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimport_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..4d836b6 --- /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.newSingleThreadExecutor(); + + 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..0ff6e7d --- /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.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(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..2939e8a --- /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.newSingleThreadExecutor(); + + 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..474888c --- /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.i("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.i("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..30ac033 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + EmptyIfJniServiceFactory factory = EmptyIfJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for EmptyIfJniServiceFactory"); + return EmptyIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..226e68b --- /dev/null +++ b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/java/tbIfaceimport/tbIfaceimportserviceexample/TbIfaceimportTestServiceApp.java @@ -0,0 +1,167 @@ +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.i(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.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbIfaceimport/tbIfaceimportserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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/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..c23e20f --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..2db2282 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + 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 new file mode 100644 index 0000000..9b8566c --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/src/main/java/tbNames/tbNames_android_client/NamEsClient.java @@ -0,0 +1,553 @@ +//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.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; + +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; + private EnumWithUnderScores m_enum_property = EnumWithUnderScores.FirstValue; + + + 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.i(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(); + + data.setClassLoader(EnumWithUnderScoresParcelable.class.getClassLoader()); + + + 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); + + EnumWithUnderScores enum_property = data.getParcelable("enum_property", EnumWithUnderScoresParcelable.class).getEnumWithUnderScores(); + onEnumProperty(enum_property); + + 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; + } + 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); + 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; + } + + + @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 + + + @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..c97b6db --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_client/src/test/java/tbNames/tbNames_android_client/NamEsClientTest.java @@ -0,0 +1,404 @@ +//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.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; +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); + EnumWithUnderScores testenum_property = EnumWithUnderScores.SecondValue; + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(testenum_property)); + + //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); + inOrderEventListener.verify(listenerMock,times(1)).onEnumPropertyChanged(testenum_property); + } + @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); + } + + @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); + } + + @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 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 + { + + 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/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 new file mode 100644 index 0000000..532c6ba --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_messenger/src/main/java/tbNames/tbNames_android_messenger/NamEsMessageType.java @@ -0,0 +1,44 @@ +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), + 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; + + 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_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 new file mode 100644 index 0000000..5b610e4 --- /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') + 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' + 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..93e36c6 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/build.gradle @@ -0,0 +1,36 @@ +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..a98d63f --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/main/java/tbNames/tbNames_android_service/NamEsServiceAdapter.java @@ -0,0 +1,422 @@ +//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.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; + +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 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<>(); + + public NamEsServiceAdapter() + { + } + + public static INamEs setService(INamEsServiceFactory 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(NamEs) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NamEsService) 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(); + 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: NamEsService::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(NamEsService) - 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 INamEsEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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; + } + 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); + + 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); + EnumWithUnderScores enum_property = mBackendService.getEnumProperty(); + + data.putParcelable("enum_property", new EnumWithUnderScoresParcelable(enum_property)); + 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 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(); + 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..2f99ed3 --- /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.i("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.i("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..d09d0fb --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NamEsServiceFactory factory = NamEsServiceFactory.get(); + Log.i(TAG, "starter: factory set for NamEsServiceFactory"); + return NamEsServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..86458e5 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_android_service/src/test/java/tbNames/tbNames_android_service/NamEsServiceAdapterTest.java @@ -0,0 +1,448 @@ +//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.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_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); + EnumWithUnderScores initenum_property = EnumWithUnderScores.SecondValue; + when(backendServiceMock.getEnumProperty()).thenReturn(initenum_property); + + + 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(); + inOrderBackendService.verify(backendServiceMock, times(1)).getEnumProperty(); + + 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); + + 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); + + } + + @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); + } + @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); + } + @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); + } + @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 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; + + 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..3bfc5e3 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/AbstractNamEs.java @@ -0,0 +1,67 @@ +package tbNames.tbNames_api; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.INamEs; +import tbNames.tbNames_api.EnumWithUnderScores; + +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 fireEnumPropertyChanged(EnumWithUnderScores newValue) { + for (INamEsEventListener listener : listeners) { + listener.onEnumPropertyChanged(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/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 new file mode 100644 index 0000000..ced64be --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEs.java @@ -0,0 +1,39 @@ +package tbNames.tbNames_api; + +import tbNames.tbNames_api.INamEsEventListener; +import tbNames.tbNames_api.EnumWithUnderScores; + +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); + + void setEnumProperty(EnumWithUnderScores enum_property); + EnumWithUnderScores getEnumProperty(); + void fireEnumPropertyChanged(EnumWithUnderScores 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..45b0f93 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/INamEsEventListener.java @@ -0,0 +1,12 @@ +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 new file mode 100644 index 0000000..c98199d --- /dev/null +++ b/goldenmaster/tbNames/tbNames_api/src/main/java/tbNames/tbNames_api/TbNamesTestHelper.java @@ -0,0 +1,19 @@ +package tbNames.tbNames_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +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 new file mode 100644 index 0000000..ae09a94 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbNames" +version = "1.0.0" + +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..06c61a3 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_client_example/src/main/java/tbNames/tbNames_client_example/TbNamesTestClientApp.java @@ -0,0 +1,305 @@ +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 tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; +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); + 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); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSomeFunction = new Button(this); + bSomeFunction.setText("SOME_FUNCTION"); + + bSomeFunction.setOnClickListener(v -> { + Log.i(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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new NamEsClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: Switch " + newValue); + } + @Override + public void onSomePropertyChanged(int newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: Some_Poperty2 " + newValue); + } + @Override + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSomeSignal2(boolean Some_Param) + { + String text = "Signal Some_Signal2 "+ " " + Some_Param; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbNames/tbNames_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..10ecde4 --- /dev/null +++ b/goldenmaster/tbNames/tbNames_impl/additions.gradle @@ -0,0 +1,19 @@ + +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..389bc1a --- /dev/null +++ b/goldenmaster/tbNames/tbNames_impl/src/main/java/tbNames/tbNames_impl/NamEsService.java @@ -0,0 +1,182 @@ +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 tbNames.tbNames_api.EnumWithUnderScores; + + +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.newSingleThreadExecutor(); + 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() + { + 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; + } + + + @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 + public void someFunction(boolean SOME_PARAM) { + Log.i(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.i(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); + } + 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"); + 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..7d5ce16 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesjniclient/NamEsJniClient.java @@ -0,0 +1,205 @@ +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 tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; +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(); + } + + @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 "); + 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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NamEsClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onSwitchChanged(boolean newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnSwitchChanged(newValue); + } + @Override + public void onSomePropertyChanged(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnSomePropertyChanged(newValue); + } + @Override + public void onSomePoperty2Changed(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnSomePoperty2Changed(newValue); + } + @Override + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnEnumPropertyChanged(newValue); + } + @Override + public void onSomeSignal(boolean 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.i(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 nativeOnEnumPropertyChanged(EnumWithUnderScores enum_property); + 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..4ff2c3b --- /dev/null +++ b/goldenmaster/tbNames/tbNamesjniservice/NamEsJniService.java @@ -0,0 +1,180 @@ +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 tbNames.tbNames_api.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; + +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.newSingleThreadExecutor(); + + 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(); + } + + + @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 + public void someFunction(boolean SOME_PARAM) { + Log.i(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.i(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(); + + 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); + + // 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 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"); + 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..ec48a66 --- /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.i("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.i("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..5491b98 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NamEsJniServiceFactory factory = NamEsJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NamEsJniServiceFactory"); + return NamEsServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..44724bf --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbNames" +version = "1.0.0" + + +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..d8771b6 --- /dev/null +++ b/goldenmaster/tbNames/tbNamesserviceexample/src/main/java/tbNames/tbNamesserviceexample/TbNamesTestServiceApp.java @@ -0,0 +1,271 @@ +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.EnumWithUnderScores; +import tbNames.tbNames_android_messenger.EnumWithUnderScoresParcelable; + +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); + 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); + + + LinearLayout methodButtonsLine = new LinearLayout(this); + methodButtonsLine.setOrientation(LinearLayout.HORIZONTAL); + Button bSomeSignal = new Button(this); + bSomeSignal.setText("SOME_SIGNAL"); + + bSomeSignal.setOnClickListener(v -> { + Log.i(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.i(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.i(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.i(TAG, "Property from service: Switch " + newValue); + } + @Override + public void onSomePropertyChanged(int newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: Some_Poperty2 " + newValue); + } + @Override + public void onEnumPropertyChanged(EnumWithUnderScores newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSomeSignal2(boolean Some_Param) + { + String text = "Signal Some_Signal2 "+ " " + Some_Param; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbNames/tbNamesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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/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..9888cd1 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/build.gradle @@ -0,0 +1,38 @@ +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' + 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 new file mode 100644 index 0000000..a7e367d --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClient.java @@ -0,0 +1,715 @@ +//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.i(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(); + 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(); + 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 != 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 != 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 != 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 != 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..d4ac017 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/main/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClient.java @@ -0,0 +1,328 @@ +//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.i(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..238a6a8 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/ParentIfClientTest.java @@ -0,0 +1,578 @@ +//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.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; +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(new SimpleLocalIfService()); + data.putParcelable("localIf", new SimpleLocalIfParcelable(testlocalIf)); + ISimpleLocalIf[] testlocalIfList = new ISimpleLocalIf[1]; + testlocalIfList[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); + data.putParcelableArray("localIfList", SimpleLocalIfParcelable.wrapArray(testlocalIfList)); + 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_impl.EmptyIfService()); + 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(new SimpleLocalIfService()); + 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(new SimpleLocalIfService()); + + 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 SimpleLocalIfService()); + 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 SimpleLocalIfService()); + + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + + 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_impl.EmptyIfService()); + 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_impl.EmptyIfService()); + + 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(new SimpleLocalIfService()); + 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 SimpleLocalIfService()); + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + 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_impl.EmptyIfService()); + 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(new SimpleLocalIfService()); + 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 SimpleLocalIfService()); + ISimpleLocalIf[] expectedResult = new ISimpleLocalIf[1]; + expectedResult[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); + + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + 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_impl.EmptyIfService()); + tbIfaceimport.tbIfaceimport_api.IEmptyIf[] expectedResult = new tbIfaceimport.tbIfaceimport_api.IEmptyIf[1]; + expectedResult[0] = tbIfaceimport.tbIfaceimport_api.TbIfaceimportTestHelper.makeTestEmptyIf(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + + 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..86dbee8 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_client/src/test/java/tbRefIfaces/tbRefIfaces_android_client/SimpleLocalIfClientTest.java @@ -0,0 +1,247 @@ +//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.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; +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..84be2e9 --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapter.java @@ -0,0 +1,515 @@ +//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(); + 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 ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..ca80362 --- /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.i("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.i("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..ba6486f --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + ParentIfServiceFactory factory = ParentIfServiceFactory.get(); + Log.i(TAG, "starter: factory set for ParentIfServiceFactory"); + return ParentIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..8d197ad --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/main/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapter.java @@ -0,0 +1,314 @@ +//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(); + 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 ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..f390003 --- /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.i("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.i("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..fe65ba3 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SimpleLocalIfServiceFactory factory = SimpleLocalIfServiceFactory.get(); + Log.i(TAG, "starter: factory set for SimpleLocalIfServiceFactory"); + return SimpleLocalIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..f65247b --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/ParentIfServiceAdapterTest.java @@ -0,0 +1,592 @@ +//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.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_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(new SimpleLocalIfService()); + 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(new SimpleLocalIfService()); + + 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 SimpleLocalIfService()); + 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 SimpleLocalIfService()); + + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + + 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_impl.EmptyIfService()); + 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_impl.EmptyIfService()); + + 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(new SimpleLocalIfService()); + + 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 SimpleLocalIfService()); + + 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(new tbIfaceimport.tbIfaceimport_impl.EmptyIfService()); + + 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_impl.EmptyIfService()); + + 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(new SimpleLocalIfService()); + 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 SimpleLocalIfService()); + data.putParcelableArray("param", SimpleLocalIfParcelable.wrapArray(testparam)); + ISimpleLocalIf[] returnedValue = new ISimpleLocalIf[1]; + returnedValue[0] = TbRefIfacesTestHelper.makeTestSimpleLocalIf(new SimpleLocalIfService()); + + + 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(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); + + + 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_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_impl.EmptyIfService()); + + + 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..a56085c --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfaces_android_service/src/test/java/tbRefIfaces/tbRefIfaces_android_service/SimpleLocalIfServiceAdapterTest.java @@ -0,0 +1,291 @@ +//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.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_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..135ae01 --- /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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new SimpleLocalIfClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: intProperty " + newValue); + } + @Override + public void onIntSignal(int param) + { + String text = "Signal intSignal "+ " " + param; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfaces_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..1aa85f3 --- /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.newSingleThreadExecutor(); + 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 != 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 != 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.i(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.i(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.i(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.i(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..7579653 --- /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.newSingleThreadExecutor(); + 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.i(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..76b09ca --- /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.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onLocalIfChanged(ISimpleLocalIf newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnLocalIfChanged(newValue); + } + @Override + public void onLocalIfListChanged(ISimpleLocalIf[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnLocalIfListChanged(newValue); + } + @Override + public void onImportedIfChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnImportedIfChanged(newValue); + } + @Override + public void onImportedIfListChanged(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnImportedIfListChanged(newValue); + } + @Override + public void onLocalIfSignal(ISimpleLocalIf param) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal localIfSignal "+ " " + param); + nativeOnLocalIfSignal(param); + } + @Override + public void onLocalIfSignalList(ISimpleLocalIf[] param) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal localIfSignalList "+ " " + param); + nativeOnLocalIfSignalList(param); + } + @Override + public void onImportedIfSignal(tbIfaceimport.tbIfaceimport_api.IEmptyIf param) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal importedIfSignal "+ " " + param); + nativeOnImportedIfSignal(param); + } + @Override + public void onImportedIfSignalList(tbIfaceimport.tbIfaceimport_api.IEmptyIf[] param) + { + Log.i(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..5aa7f14 --- /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.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onIntPropertyChanged(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnIntPropertyChanged(newValue); + } + @Override + public void onIntSignal(int param) + { + Log.i(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..fbe142f --- /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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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..69fd165 --- /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.i("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.i("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..db12ea1 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + ParentIfJniServiceFactory factory = ParentIfJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for ParentIfJniServiceFactory"); + return ParentIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..5e6f523 --- /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.newSingleThreadExecutor(); + + 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.i(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..b3972ee --- /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.i("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.i("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..d2307ab --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SimpleLocalIfJniServiceFactory factory = SimpleLocalIfJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SimpleLocalIfJniServiceFactory"); + return SimpleLocalIfServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..c82fecc --- /dev/null +++ b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/java/tbRefIfaces/tbRefIfacesserviceexample/TbRefIfacesTestServiceApp.java @@ -0,0 +1,201 @@ +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.i(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.i(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.i(TAG, "Property from service: intProperty " + newValue); + } + @Override + public void onIntSignal(int param) + { + String text = "Signal intSignal "+ " " + param; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbRefIfaces/tbRefIfacesserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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/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..5fb3801 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..a400c4c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + 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 new file mode 100644 index 0000000..a124fc3 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClient.java @@ -0,0 +1,334 @@ +//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.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; + +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.i(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..a48e947 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClient.java @@ -0,0 +1,466 @@ +//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.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.i(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()); + + + 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()); + + 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()); + 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..3199708 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClient.java @@ -0,0 +1,336 @@ +//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.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.i(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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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..986ad54 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/main/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClient.java @@ -0,0 +1,470 @@ +//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.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.i(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()); + + + 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()); + + 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()); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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..66f041c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum1InterfaceClientTest.java @@ -0,0 +1,263 @@ +//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.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; +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); + } + @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..dc6ef7e --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameEnum2InterfaceClientTest.java @@ -0,0 +1,366 @@ +//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.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; +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); + } + @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); + } + + @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()); + + 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..d1c7366 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct1InterfaceClientTest.java @@ -0,0 +1,263 @@ +//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.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; +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)); + } + @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..296d01c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_client/src/test/java/tbSame1/tbSame1_android_client/SameStruct2InterfaceClientTest.java @@ -0,0 +1,366 @@ +//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.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; +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)); + } + @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); + } + + @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()); + + 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/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/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/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/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/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/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/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_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..ca48eda --- /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') + 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' + 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..245ec8f --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/build.gradle @@ -0,0 +1,36 @@ +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..f371dda --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapter.java @@ -0,0 +1,318 @@ +//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.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; + +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 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<>(); + + public SameEnum1InterfaceServiceAdapter() + { + } + + public static ISameEnum1Interface setService(ISameEnum1InterfaceServiceFactory 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(SameEnum1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum1InterfaceService) 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(); + 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: SameEnum1InterfaceService::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(SameEnum1InterfaceService) - 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 ISameEnum1InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..6710322 --- /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.i("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.i("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..187c556 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum1InterfaceServiceFactory factory = SameEnum1InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..c1ed470 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapter.java @@ -0,0 +1,389 @@ +//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.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 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<>(); + + public SameEnum2InterfaceServiceAdapter() + { + } + + public static ISameEnum2Interface setService(ISameEnum2InterfaceServiceFactory 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(SameEnum2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum2InterfaceService) 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(); + 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: SameEnum2InterfaceService::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(SameEnum2InterfaceService) - 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 ISameEnum2InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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()); + 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..4ab1136 --- /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.i("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.i("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..c29ea81 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum2InterfaceServiceFactory factory = SameEnum2InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..406b2d4 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapter.java @@ -0,0 +1,318 @@ +//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.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 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<>(); + + public SameStruct1InterfaceServiceAdapter() + { + } + + public static ISameStruct1Interface setService(ISameStruct1InterfaceServiceFactory 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(SameStruct1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct1InterfaceService) 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(); + 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: SameStruct1InterfaceService::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(SameStruct1InterfaceService) - 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 ISameStruct1InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..e781bb2 --- /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.i("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.i("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..3aeee7b --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct1InterfaceServiceFactory factory = SameStruct1InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..e6dcbcd --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/main/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapter.java @@ -0,0 +1,389 @@ +//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.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 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<>(); + + public SameStruct2InterfaceServiceAdapter() + { + } + + public static ISameStruct2Interface setService(ISameStruct2InterfaceServiceFactory 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(SameStruct2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct2InterfaceService) 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(); + 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: SameStruct2InterfaceService::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(SameStruct2InterfaceService) - 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 ISameStruct2InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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()); + 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..c22be3a --- /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.i("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.i("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..78484ee --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct2InterfaceServiceFactory factory = SameStruct2InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..0dd0f4a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -0,0 +1,308 @@ +//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.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_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); + } + @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..dca942c --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -0,0 +1,407 @@ +//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.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_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()); + 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); + } + @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); + } + @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()); + + 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..7e56738 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -0,0 +1,309 @@ +//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.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_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); + } + @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..fe868e8 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_android_service/src/test/java/tbSame1/tbSame1_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -0,0 +1,409 @@ +//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.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_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()); + // 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); + } + @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); + } + @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()); + + 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..87ef7a1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum1Interface.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameEnum1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum1Interface; +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..53077a1 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameEnum2Interface.java @@ -0,0 +1,56 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameEnum2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameEnum2Interface; +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..e8bb2bd --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct1Interface.java @@ -0,0 +1,42 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameStruct1InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct1Interface; +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..e905af3 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/AbstractSameStruct2Interface.java @@ -0,0 +1,56 @@ +package tbSame1.tbSame1_api; + +import tbSame1.tbSame1_api.ISameStruct2InterfaceEventListener; +import tbSame1.tbSame1_api.ISameStruct2Interface; +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..efbd1db --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_api/src/main/java/tbSame1/tbSame1_api/TbSame1TestHelper.java @@ -0,0 +1,60 @@ +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; + } + + 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 new file mode 100644 index 0000000..683aa3b --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbSame1" +version = "1.0.0" + +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..a1d370a --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_client_example/src/main/java/tbSame1/tbSame1_client_example/TbSame1TestClientApp.java @@ -0,0 +1,228 @@ +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 tbSame1.tbSame1_api.Struct1; +import tbSame1.tbSame1_android_messenger.Struct1Parcelable; +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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new SameStruct1InterfaceClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..2b12711 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/additions.gradle @@ -0,0 +1,19 @@ + +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..538f4fe --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum1InterfaceService.java @@ -0,0 +1,86 @@ +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.Enum1; + + +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.newSingleThreadExecutor(); + 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.i(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..faf32ff --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameEnum2InterfaceService.java @@ -0,0 +1,131 @@ +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.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.newSingleThreadExecutor(); + 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.i(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.i(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..cd0e153 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1_impl/src/main/java/tbSame1/tbSame1_impl/SameStruct1InterfaceService.java @@ -0,0 +1,87 @@ +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 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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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.i(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..9fecfb6 --- /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 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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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.i(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.i(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..15a1824 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum1InterfaceJniClient.java @@ -0,0 +1,119 @@ +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.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum1InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.i(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..2c64f25 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameEnum2InterfaceJniClient.java @@ -0,0 +1,167 @@ +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.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; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum2InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(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..ef8e7fe --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct1InterfaceJniClient.java @@ -0,0 +1,119 @@ +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_android_messenger.Struct1Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct1InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.i(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..35fcafa --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniclient/SameStruct2InterfaceJniClient.java @@ -0,0 +1,167 @@ +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_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct2InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Struct2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(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..58a2878 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum1InterfaceJniService.java @@ -0,0 +1,92 @@ +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.Enum1; +import tbSame1.tbSame1_android_messenger.Enum1Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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..482c5b7 --- /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.i("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.i("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..051dbfb --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum1InterfaceJniServiceFactory factory = SameEnum1InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..f75bc33 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameEnum2InterfaceJniService.java @@ -0,0 +1,136 @@ +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.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; +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.newSingleThreadExecutor(); + + 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.i(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.i(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..c0cb591 --- /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.i("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.i("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..e3fff0d --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum2InterfaceJniServiceFactory factory = SameEnum2InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..c4cbb41 --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct1InterfaceJniService.java @@ -0,0 +1,92 @@ +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_android_messenger.Struct1Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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..f0ba3f9 --- /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.i("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.i("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..4413ac1 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct1InterfaceJniServiceFactory factory = SameStruct1InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..44aae5d --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1jniservice/SameStruct2InterfaceJniService.java @@ -0,0 +1,136 @@ +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_android_messenger.Struct1Parcelable; +import tbSame1.tbSame1_api.Struct2; +import tbSame1.tbSame1_android_messenger.Struct2Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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.i(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..178077b --- /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.i("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.i("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..53cd704 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct2InterfaceJniServiceFactory factory = SameStruct2InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..6d8bc4e --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbSame1" +version = "1.0.0" + + +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..90e30ad --- /dev/null +++ b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/java/tbSame1/tbSame1serviceexample/TbSame1TestServiceApp.java @@ -0,0 +1,203 @@ +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.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.i(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.i(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.i(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbSame1/tbSame1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..91ff31b --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..afbc515 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + 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 new file mode 100644 index 0000000..8bdef54 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClient.java @@ -0,0 +1,334 @@ +//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.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; + +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.i(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..68d6b03 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClient.java @@ -0,0 +1,466 @@ +//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.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.i(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()); + + + 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()); + + 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()); + 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..144dadb --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClient.java @@ -0,0 +1,336 @@ +//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.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.i(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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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..5ba0b46 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/main/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClient.java @@ -0,0 +1,470 @@ +//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.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.i(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()); + + + 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()); + + 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()); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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..dea7280 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum1InterfaceClientTest.java @@ -0,0 +1,263 @@ +//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.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; +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); + } + @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..fec620f --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameEnum2InterfaceClientTest.java @@ -0,0 +1,366 @@ +//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.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; +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); + } + @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); + } + + @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()); + + 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..6709813 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct1InterfaceClientTest.java @@ -0,0 +1,263 @@ +//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.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; +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)); + } + @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..6ff5131 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_client/src/test/java/tbSame2/tbSame2_android_client/SameStruct2InterfaceClientTest.java @@ -0,0 +1,366 @@ +//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.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; +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)); + } + @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); + } + + @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()); + + 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/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/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/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/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/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/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/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_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..9f460c7 --- /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') + 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' + 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..b7b46a0 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/build.gradle @@ -0,0 +1,36 @@ +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..58762b1 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapter.java @@ -0,0 +1,318 @@ +//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.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; + +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 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<>(); + + public SameEnum1InterfaceServiceAdapter() + { + } + + public static ISameEnum1Interface setService(ISameEnum1InterfaceServiceFactory 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(SameEnum1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum1InterfaceService) 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(); + 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: SameEnum1InterfaceService::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(SameEnum1InterfaceService) - 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 ISameEnum1InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..ce76741 --- /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.i("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.i("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..2e5c6a6 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum1InterfaceServiceFactory factory = SameEnum1InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..5fa02bc --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapter.java @@ -0,0 +1,389 @@ +//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.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 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<>(); + + public SameEnum2InterfaceServiceAdapter() + { + } + + public static ISameEnum2Interface setService(ISameEnum2InterfaceServiceFactory 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(SameEnum2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameEnum2InterfaceService) 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(); + 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: SameEnum2InterfaceService::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(SameEnum2InterfaceService) - 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 ISameEnum2InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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()); + 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..b2c064f --- /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.i("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.i("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..b1e8ab5 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum2InterfaceServiceFactory factory = SameEnum2InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..4e31854 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapter.java @@ -0,0 +1,318 @@ +//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.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 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<>(); + + public SameStruct1InterfaceServiceAdapter() + { + } + + public static ISameStruct1Interface setService(ISameStruct1InterfaceServiceFactory 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(SameStruct1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct1InterfaceService) 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(); + 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: SameStruct1InterfaceService::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(SameStruct1InterfaceService) - 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 ISameStruct1InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..3cd3076 --- /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.i("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.i("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..8239274 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct1InterfaceServiceFactory factory = SameStruct1InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..97919fa --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/main/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapter.java @@ -0,0 +1,389 @@ +//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.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 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<>(); + + public SameStruct2InterfaceServiceAdapter() + { + } + + public static ISameStruct2Interface setService(ISameStruct2InterfaceServiceFactory 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(SameStruct2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SameStruct2InterfaceService) 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(); + 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: SameStruct2InterfaceService::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(SameStruct2InterfaceService) - 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 ISameStruct2InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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()); + 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..4d39bd9 --- /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.i("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.i("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..a78b49a --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct2InterfaceServiceFactory factory = SameStruct2InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..501b0ab --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum1InterfaceServiceAdapterTest.java @@ -0,0 +1,308 @@ +//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.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_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); + } + @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..19b3b44 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameEnum2InterfaceServiceAdapterTest.java @@ -0,0 +1,407 @@ +//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.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_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()); + 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); + } + @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); + } + @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()); + + 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..c927b32 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct1InterfaceServiceAdapterTest.java @@ -0,0 +1,309 @@ +//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.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_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); + } + @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..7b1ab42 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_android_service/src/test/java/tbSame2/tbSame2_android_service/SameStruct2InterfaceServiceAdapterTest.java @@ -0,0 +1,409 @@ +//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.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_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()); + // 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); + } + @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); + } + @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()); + + 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..535d3b8 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum1Interface.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameEnum1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum1Interface; +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..a32ff4f --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameEnum2Interface.java @@ -0,0 +1,56 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameEnum2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameEnum2Interface; +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..8715dbe --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct1Interface.java @@ -0,0 +1,42 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameStruct1InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct1Interface; +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..f553fe6 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/AbstractSameStruct2Interface.java @@ -0,0 +1,56 @@ +package tbSame2.tbSame2_api; + +import tbSame2.tbSame2_api.ISameStruct2InterfaceEventListener; +import tbSame2.tbSame2_api.ISameStruct2Interface; +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..ef062de --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_api/src/main/java/tbSame2/tbSame2_api/TbSame2TestHelper.java @@ -0,0 +1,60 @@ +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; + } + + 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 new file mode 100644 index 0000000..ce41936 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbSame2" +version = "1.0.0" + +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..5d0f2bb --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_client_example/src/main/java/tbSame2/tbSame2_client_example/TbSame2TestClientApp.java @@ -0,0 +1,228 @@ +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 tbSame2.tbSame2_api.Struct1; +import tbSame2.tbSame2_android_messenger.Struct1Parcelable; +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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new SameStruct1InterfaceClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..d5eea51 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/additions.gradle @@ -0,0 +1,19 @@ + +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..44c8992 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum1InterfaceService.java @@ -0,0 +1,86 @@ +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.Enum1; + + +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.newSingleThreadExecutor(); + 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.i(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..40c9576 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameEnum2InterfaceService.java @@ -0,0 +1,131 @@ +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.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.newSingleThreadExecutor(); + 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.i(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.i(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..4327e76 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2_impl/src/main/java/tbSame2/tbSame2_impl/SameStruct1InterfaceService.java @@ -0,0 +1,87 @@ +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 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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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.i(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..db1b3a5 --- /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 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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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.i(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.i(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..eee2fe2 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum1InterfaceJniClient.java @@ -0,0 +1,119 @@ +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.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum1InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.i(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..88be680 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameEnum2InterfaceJniClient.java @@ -0,0 +1,167 @@ +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.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; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameEnum2InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Enum1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Enum2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Enum1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Enum1 param1, Enum2 param2) + { + Log.i(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..a9dff37 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct1InterfaceJniClient.java @@ -0,0 +1,119 @@ +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_android_messenger.Struct1Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct1InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.i(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..e2e4e1a --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniclient/SameStruct2InterfaceJniClient.java @@ -0,0 +1,167 @@ +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_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SameStruct2InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(Struct2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(Struct2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(Struct1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(Struct1 param1, Struct2 param2) + { + Log.i(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..44c04a6 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum1InterfaceJniService.java @@ -0,0 +1,92 @@ +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.Enum1; +import tbSame2.tbSame2_android_messenger.Enum1Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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..272d0ef --- /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.i("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.i("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..9c35650 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum1InterfaceJniServiceFactory factory = SameEnum1InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum1InterfaceJniServiceFactory"); + return SameEnum1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..cc67216 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameEnum2InterfaceJniService.java @@ -0,0 +1,136 @@ +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.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; +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.newSingleThreadExecutor(); + + 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.i(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.i(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..a5c7cbf --- /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.i("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.i("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..1d26dd7 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameEnum2InterfaceJniServiceFactory factory = SameEnum2InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameEnum2InterfaceJniServiceFactory"); + return SameEnum2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..eba8102 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct1InterfaceJniService.java @@ -0,0 +1,92 @@ +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_android_messenger.Struct1Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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..7ff0e1c --- /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.i("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.i("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..311cbe5 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct1InterfaceJniServiceFactory factory = SameStruct1InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct1InterfaceJniServiceFactory"); + return SameStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..f3434d3 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2jniservice/SameStruct2InterfaceJniService.java @@ -0,0 +1,136 @@ +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_android_messenger.Struct1Parcelable; +import tbSame2.tbSame2_api.Struct2; +import tbSame2.tbSame2_android_messenger.Struct2Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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.i(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..1876568 --- /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.i("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.i("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..94522bc --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SameStruct2InterfaceJniServiceFactory factory = SameStruct2InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SameStruct2InterfaceJniServiceFactory"); + return SameStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..9be6ed4 --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbSame2" +version = "1.0.0" + + +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..9be726f --- /dev/null +++ b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/java/tbSame2/tbSame2serviceexample/TbSame2TestServiceApp.java @@ -0,0 +1,203 @@ +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.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.i(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.i(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.i(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onSig1(Struct1 param1) + { + String text = "Signal sig1 "+ " " + param1; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbSame2/tbSame2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..52f0d68 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..418a6b6 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + testImplementation project(':tbSimple_impl') +} 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..6283085 --- /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.i(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..e9d9e28 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClient.java @@ -0,0 +1,331 @@ +//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.i(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..a8c3cf0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClient.java @@ -0,0 +1,346 @@ +//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.i(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..eb28f0b --- /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.i(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..424d7a6 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClient.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.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.i(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..49363b5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/SimpleInterfaceClient.java @@ -0,0 +1,1304 @@ +//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.i(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_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: { + + 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 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); + 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..5a8cb1b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/main/java/tbSimple/tbSimple_android_client/VoidInterfaceClient.java @@ -0,0 +1,273 @@ +//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.i(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..6d4403b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/EmptyInterfaceClientTest.java @@ -0,0 +1,167 @@ +//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.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; +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..1abd4e2 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoOperationsInterfaceClientTest.java @@ -0,0 +1,265 @@ +//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.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; +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); + } + @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); + } + + @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..95be451 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoPropertiesInterfaceClientTest.java @@ -0,0 +1,280 @@ +//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.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; +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..71c089d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/NoSignalsInterfaceClientTest.java @@ -0,0 +1,318 @@ +//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.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; +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); + } + @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); + } + + @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..427e321 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleArrayInterfaceClientTest.java @@ -0,0 +1,991 @@ +//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.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; +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); + } + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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 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..dba17f3 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/SimpleInterfaceClientTest.java @@ -0,0 +1,1010 @@ +//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.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; +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); + } + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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); + } + + @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 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 + 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..06c2e07 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_client/src/test/java/tbSimple/tbSimple_android_client/VoidInterfaceClientTest.java @@ -0,0 +1,219 @@ +//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.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; +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/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/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/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/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/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/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/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/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/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/SimpleInterfaceMessageType.java b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java new file mode 100644 index 0000000..f36499d --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_messenger/src/main/java/tbSimple/tbSimple_android_messenger/SimpleInterfaceMessageType.java @@ -0,0 +1,74 @@ +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_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; + + 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/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/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_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 new file mode 100644 index 0000000..f0e18ee --- /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') + 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' + 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..f0caa25 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/build.gradle @@ -0,0 +1,36 @@ +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..3734dc4 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapter.java @@ -0,0 +1,251 @@ +//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 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<>(); + + public EmptyInterfaceServiceAdapter() + { + } + + public static IEmptyInterface setService(IEmptyInterfaceServiceFactory 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(EmptyInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(EmptyInterfaceService) 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(); + 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: EmptyInterfaceService::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(EmptyInterfaceService) - 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 IEmptyInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..3968576 --- /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.i("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.i("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..7465216 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + EmptyInterfaceServiceFactory factory = EmptyInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for EmptyInterfaceServiceFactory"); + return EmptyInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..04c9c1c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapter.java @@ -0,0 +1,317 @@ +//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 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<>(); + + public NoOperationsInterfaceServiceAdapter() + { + } + + public static INoOperationsInterface setService(INoOperationsInterfaceServiceFactory 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(NoOperationsInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NoOperationsInterfaceService) 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(); + 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: NoOperationsInterfaceService::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(NoOperationsInterfaceService) - 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 INoOperationsInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..2102951 --- /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.i("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.i("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..75831a6 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NoOperationsInterfaceServiceFactory factory = NoOperationsInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for NoOperationsInterfaceServiceFactory"); + return NoOperationsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..1aeb4ad --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapter.java @@ -0,0 +1,325 @@ +//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 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<>(); + + public NoPropertiesInterfaceServiceAdapter() + { + } + + public static INoPropertiesInterface setService(INoPropertiesInterfaceServiceFactory 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(NoPropertiesInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NoPropertiesInterfaceService) 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(); + 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: NoPropertiesInterfaceService::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(NoPropertiesInterfaceService) - 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 INoPropertiesInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..0d81395 --- /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.i("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.i("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..a4eaf24 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NoPropertiesInterfaceServiceFactory factory = NoPropertiesInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for NoPropertiesInterfaceServiceFactory"); + return NoPropertiesInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..61bfab9 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapter.java @@ -0,0 +1,351 @@ +//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 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<>(); + + public NoSignalsInterfaceServiceAdapter() + { + } + + public static INoSignalsInterface setService(INoSignalsInterfaceServiceFactory 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(NoSignalsInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NoSignalsInterfaceService) 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(); + 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: NoSignalsInterfaceService::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(NoSignalsInterfaceService) - 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 INoSignalsInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..75b793c --- /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.i("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.i("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..ee7878f --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NoSignalsInterfaceServiceFactory factory = NoSignalsInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for NoSignalsInterfaceServiceFactory"); + return NoSignalsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..c6dc78b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapter.java @@ -0,0 +1,778 @@ +//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 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<>(); + + public SimpleArrayInterfaceServiceAdapter() + { + } + + public static ISimpleArrayInterface setService(ISimpleArrayInterfaceServiceFactory 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(SimpleArrayInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SimpleArrayInterfaceService) 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(); + 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: SimpleArrayInterfaceService::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(SimpleArrayInterfaceService) - 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 ISimpleArrayInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..dc93d53 --- /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.i("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.i("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..8923058 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SimpleArrayInterfaceServiceFactory factory = SimpleArrayInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SimpleArrayInterfaceServiceFactory"); + return SimpleArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..7420f58 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapter.java @@ -0,0 +1,809 @@ +//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 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<>(); + + public SimpleInterfaceServiceAdapter() + { + } + + public static ISimpleInterface setService(ISimpleInterfaceServiceFactory 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(SimpleInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(SimpleInterfaceService) 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(); + 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: SimpleInterfaceService::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(SimpleInterfaceService) - 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 ISimpleInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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_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: { + + 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..aebf174 --- /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.i("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.i("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..9cdf51e --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SimpleInterfaceServiceFactory factory = SimpleInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for SimpleInterfaceServiceFactory"); + return SimpleInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..d1d0831 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/main/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapter.java @@ -0,0 +1,285 @@ +//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 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<>(); + + public VoidInterfaceServiceAdapter() + { + } + + public static IVoidInterface setService(IVoidInterfaceServiceFactory 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(VoidInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(VoidInterfaceService) 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(); + 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: VoidInterfaceService::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(VoidInterfaceService) - 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 IVoidInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..fb6398e --- /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.i("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.i("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..1b78ee1 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + VoidInterfaceServiceFactory factory = VoidInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for VoidInterfaceServiceFactory"); + return VoidInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..516ef8c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/EmptyInterfaceServiceAdapterTest.java @@ -0,0 +1,220 @@ +//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.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_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..971067f --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoOperationsInterfaceServiceAdapterTest.java @@ -0,0 +1,327 @@ +//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.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_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); + } + @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); + } + @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..36bce5b --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoPropertiesInterfaceServiceAdapterTest.java @@ -0,0 +1,304 @@ +//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.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_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..8e323fe --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/NoSignalsInterfaceServiceAdapterTest.java @@ -0,0 +1,352 @@ +//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.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_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); + } + @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); + } + @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..93e4141 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleArrayInterfaceServiceAdapterTest.java @@ -0,0 +1,1025 @@ +//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.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_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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + + @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..f5562ad --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/SimpleInterfaceServiceAdapterTest.java @@ -0,0 +1,1012 @@ +//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.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_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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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); + } + @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 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()); + 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..d6a701e --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_android_service/src/test/java/tbSimple/tbSimple_android_service/VoidInterfaceServiceAdapterTest.java @@ -0,0 +1,253 @@ +//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.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_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..e8a3b29 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractEmptyInterface.java @@ -0,0 +1,24 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.IEmptyInterfaceEventListener; +import tbSimple.tbSimple_api.IEmptyInterface; + +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..67d104c --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoOperationsInterface.java @@ -0,0 +1,52 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoOperationsInterfaceEventListener; +import tbSimple.tbSimple_api.INoOperationsInterface; + +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..d23f8c7 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoPropertiesInterface.java @@ -0,0 +1,38 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoPropertiesInterfaceEventListener; +import tbSimple.tbSimple_api.INoPropertiesInterface; + +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..7fe1beb --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractNoSignalsInterface.java @@ -0,0 +1,38 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.INoSignalsInterfaceEventListener; +import tbSimple.tbSimple_api.INoSignalsInterface; + +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..8e8b4c3 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleArrayInterface.java @@ -0,0 +1,143 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.ISimpleArrayInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleArrayInterface; + +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..13614b0 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractSimpleInterface.java @@ -0,0 +1,136 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.ISimpleInterfaceEventListener; +import tbSimple.tbSimple_api.ISimpleInterface; + +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..9006dff --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/AbstractVoidInterface.java @@ -0,0 +1,31 @@ +package tbSimple.tbSimple_api; + +import tbSimple.tbSimple_api.IVoidInterfaceEventListener; +import tbSimple.tbSimple_api.IVoidInterface; + +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..1b0a95a --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/ISimpleInterface.java @@ -0,0 +1,76 @@ +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 funcNoParams(); + CompletableFuture funcNoParamsAsync(); + 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..24df438 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_api/src/main/java/tbSimple/tbSimple_api/TbSimpleTestHelper.java @@ -0,0 +1,88 @@ +package tbSimple.tbSimple_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +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 new file mode 100644 index 0000000..f09f20f --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbSimple" +version = "1.0.0" + +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..9b710c6 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_client_example/src/main/java/tbSimple/tbSimple_client_example/TbSimpleTestClientApp.java @@ -0,0 +1,208 @@ +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 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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new VoidInterfaceClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimple_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..897e0e4 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/additions.gradle @@ -0,0 +1,19 @@ + +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..a714dfd --- /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.newSingleThreadExecutor(); + + 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..eb9368b --- /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.newSingleThreadExecutor(); + 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..7bf86bf --- /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.newSingleThreadExecutor(); + + public NoPropertiesInterfaceService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.i(TAG, "request method funcVoid called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.i(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..f0fb250 --- /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.newSingleThreadExecutor(); + 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.i(TAG, "request method funcVoid called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcVoidAsync() { + return CompletableFuture.runAsync( + () -> { funcVoid(); }, + executor); + } + + @Override + public boolean funcBool(boolean paramBool) { + Log.i(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..dd5f2f4 --- /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.newSingleThreadExecutor(); + 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.i(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.i(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.i(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.i(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.i(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.i(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.i(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.i(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..b35617f --- /dev/null +++ b/goldenmaster/tbSimple/tbSimple_impl/src/main/java/tbSimple/tbSimple_impl/SimpleInterfaceService.java @@ -0,0 +1,419 @@ +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.newSingleThreadExecutor(); + 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.i(TAG, "request method funcNoReturnValue called, returnig default"); + return ; + } + + @Override + public CompletableFuture funcNoReturnValueAsync(boolean paramBool) { + return CompletableFuture.runAsync( + () -> { funcNoReturnValue(paramBool); }, + executor); + } + + @Override + public boolean funcNoParams() { + Log.i(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.i(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.i(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.i(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.i(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.i(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.i(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.i(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.i(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..982e58b --- /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.newSingleThreadExecutor(); + + public VoidInterfaceService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.i(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..cce3bd7 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/EmptyInterfaceJniClient.java @@ -0,0 +1,71 @@ +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new EmptyInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(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..bb361af --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoOperationsInterfaceJniClient.java @@ -0,0 +1,125 @@ +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NoOperationsInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onSigVoid() + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + nativeOnSigVoid(); + } + @Override + public void onSigBool(boolean paramBool) + { + Log.i(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..e60b6bd --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoPropertiesInterfaceJniClient.java @@ -0,0 +1,123 @@ +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NoPropertiesInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onSigVoid() + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigVoid "); + nativeOnSigVoid(); + } + @Override + public void onSigBool(boolean paramBool) + { + Log.i(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..0f60c76 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/NoSignalsInterfaceJniClient.java @@ -0,0 +1,149 @@ +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NoSignalsInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int newValue) + { + Log.i(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..744235f --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleArrayInterfaceJniClient.java @@ -0,0 +1,459 @@ +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SimpleArrayInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropInt32Changed(int[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt32Changed(newValue); + } + @Override + public void onPropInt64Changed(long[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt64Changed(newValue); + } + @Override + public void onPropFloatChanged(float[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropFloat32Changed(float[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat32Changed(newValue); + } + @Override + public void onPropFloat64Changed(double[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat64Changed(newValue); + } + @Override + public void onPropStringChanged(String[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onPropReadOnlyStringChanged(String newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropReadOnlyStringChanged(newValue); + } + @Override + public void onSigBool(boolean[] paramBool) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(int[] paramInt) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigInt32(int[] paramInt32) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); + nativeOnSigInt32(paramInt32); + } + @Override + public void onSigInt64(long[] paramInt64) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); + nativeOnSigInt64(paramInt64); + } + @Override + public void onSigFloat(float[] paramFloat) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigFloat32(float[] paramFloa32) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloa32); + nativeOnSigFloat32(paramFloa32); + } + @Override + public void onSigFloat64(double[] paramFloat64) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); + nativeOnSigFloat64(paramFloat64); + } + @Override + public void onSigString(String[] paramString) + { + Log.i(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..a7f5772 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/SimpleInterfaceJniClient.java @@ -0,0 +1,477 @@ +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 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) + { + 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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new SimpleInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(boolean newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropInt32Changed(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt32Changed(newValue); + } + @Override + public void onPropInt64Changed(long newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropInt64Changed(newValue); + } + @Override + public void onPropFloatChanged(float newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropFloat32Changed(float newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat32Changed(newValue); + } + @Override + public void onPropFloat64Changed(double newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloat64Changed(newValue); + } + @Override + public void onPropStringChanged(String newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onSigBool(boolean paramBool) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(int paramInt) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigInt32(int paramInt32) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt32 "+ " " + paramInt32); + nativeOnSigInt32(paramInt32); + } + @Override + public void onSigInt64(long paramInt64) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt64 "+ " " + paramInt64); + nativeOnSigInt64(paramInt64); + } + @Override + public void onSigFloat(float paramFloat) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigFloat32(float paramFloat32) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat32 "+ " " + paramFloat32); + nativeOnSigFloat32(paramFloat32); + } + @Override + public void onSigFloat64(double paramFloat64) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat64 "+ " " + paramFloat64); + nativeOnSigFloat64(paramFloat64); + } + @Override + public void onSigString(String paramString) + { + Log.i(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 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); + 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..8a12387 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniclient/VoidInterfaceJniClient.java @@ -0,0 +1,97 @@ +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new VoidInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onSigVoid() + { + Log.i(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..247a5de --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/EmptyInterfaceJniService.java @@ -0,0 +1,48 @@ +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.newSingleThreadExecutor(); + + 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..35af06e --- /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.i("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.i("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..c94b51b --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + EmptyInterfaceJniServiceFactory factory = EmptyInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for EmptyInterfaceJniServiceFactory"); + return EmptyInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..a88f436 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoOperationsInterfaceJniService.java @@ -0,0 +1,104 @@ +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.newSingleThreadExecutor(); + + 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..8cf3c1e --- /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.i("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.i("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..f8ac629 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NoOperationsInterfaceJniServiceFactory factory = NoOperationsInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NoOperationsInterfaceJniServiceFactory"); + return NoOperationsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..97cfe46 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoPropertiesInterfaceJniService.java @@ -0,0 +1,86 @@ +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.newSingleThreadExecutor(); + + public NoPropertiesInterfaceJniService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.i(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.i(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..9fe3379 --- /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.i("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.i("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..b92c420 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NoPropertiesInterfaceJniServiceFactory factory = NoPropertiesInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NoPropertiesInterfaceJniServiceFactory"); + return NoPropertiesInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..e77cf05 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/NoSignalsInterfaceJniService.java @@ -0,0 +1,122 @@ +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.newSingleThreadExecutor(); + + 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.i(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.i(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..7862698 --- /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.i("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.i("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..89caaf9 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NoSignalsInterfaceJniServiceFactory factory = NoSignalsInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NoSignalsInterfaceJniServiceFactory"); + return NoSignalsInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..3dde9e5 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleArrayInterfaceJniService.java @@ -0,0 +1,407 @@ +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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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.i(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.i(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.i(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.i(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..37749d4 --- /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.i("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.i("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..adb27aa --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SimpleArrayInterfaceJniServiceFactory factory = SimpleArrayInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SimpleArrayInterfaceJniServiceFactory"); + return SimpleArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..86731be --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/SimpleInterfaceJniService.java @@ -0,0 +1,412 @@ +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.newSingleThreadExecutor(); + + 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.i(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 funcNoParams() { + Log.i(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.i(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.i(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.i(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.i(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.i(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.i(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.i(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.i(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 nativeFuncNoParams(); + 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..b855c59 --- /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.i("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.i("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..b051a18 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + SimpleInterfaceJniServiceFactory factory = SimpleInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for SimpleInterfaceJniServiceFactory"); + return SimpleInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..4382294 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimplejniservice/VoidInterfaceJniService.java @@ -0,0 +1,67 @@ +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.newSingleThreadExecutor(); + + public VoidInterfaceJniService() + { + fire_readyStatusChanged(true); + } + // methods + + @Override + public void funcVoid() { + Log.i(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..501faad --- /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.i("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.i("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..e07f053 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + VoidInterfaceJniServiceFactory factory = VoidInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for VoidInterfaceJniServiceFactory"); + return VoidInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..1711e21 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "tbSimple" +version = "1.0.0" + + +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..4853104 --- /dev/null +++ b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/java/tbSimple/tbSimpleserviceexample/TbSimpleTestServiceApp.java @@ -0,0 +1,183 @@ +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.i(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.i(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.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/tbSimple/tbSimpleserviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..6d71b2c --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..f163874 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + 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 new file mode 100644 index 0000000..84bba02 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArray2InterfaceClient.java @@ -0,0 +1,851 @@ +//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.i(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(StructBoolParcelable.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(StructIntParcelable.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(StructFloatParcelable.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(StructStringParcelable.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(Enum0Parcelable.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 != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) + { + 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 != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) + { + 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 != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) + { + 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 != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) + { + 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 != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) + { + 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 != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) + { + 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 != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) + { + 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 != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) + { + 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 != null && ! m_propEnum.equals(propEnum)) + || (m_propEnum == null && propEnum != null )) + { + 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 != null && ! m_propEnum.equals(propEnum)) + || (m_propEnum == null && propEnum != null )) + { + 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 new file mode 100644 index 0000000..7436d3e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructArrayInterfaceClient.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.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.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[]{}; + private Enum0[] m_propEnum = new Enum0[]{}; + + + 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.i(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()); + + + 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); + + Enum0[] propEnum = Enum0Parcelable.unwrapArray((Enum0Parcelable[])data.getParcelableArray("propEnum", Enum0Parcelable.class)); + onPropEnum(propEnum); + + 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; + } + 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()); + + 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 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()); + 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; + + } + 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); + 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; + } + + + @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 + + + @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 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; + } + + @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); + } + 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 new file mode 100644 index 0000000..4a74fbb --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/main/java/testbed1/testbed1_android_client/StructInterfaceClient.java @@ -0,0 +1,726 @@ +//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.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 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.i(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()); + + + 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 != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) + { + 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 != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) + { + 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 != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) + { + 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 != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) + { + 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 != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) + { + 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 != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) + { + 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 != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) + { + 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 != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) + { + 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/StructArray2InterfaceClientTest.java b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java new file mode 100644 index 0000000..02b11ae --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArray2InterfaceClientTest.java @@ -0,0 +1,655 @@ +//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.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; +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 new file mode 100644 index 0000000..30bc747 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructArrayInterfaceClientTest.java @@ -0,0 +1,696 @@ +//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.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.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; +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)); + Enum0[] testpropEnum = new Enum0[1]; + testpropEnum[0] = Enum0.Value1; + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(testpropEnum)); + + //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)); + inOrderEventListener.verify(listenerMock,times(1)).onPropEnumChanged(testpropEnum); + } + @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); + } + + @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); + } + + @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); + } + + @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 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 + { + + 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)); + +} + @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); + +} + + + 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()); + + } + + + 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 new file mode 100644 index 0000000..dd33129 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_client/src/test/java/testbed1/testbed1_android_client/StructInterfaceClientTest.java @@ -0,0 +1,569 @@ +//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.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.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; +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)); + } + @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); + } + + @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); + } + + @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); + } + + @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/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 new file mode 100644 index 0000000..30844fb --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_messenger/src/main/java/testbed1/testbed1_android_messenger/StructArrayInterfaceMessageType.java @@ -0,0 +1,55 @@ +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), + 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; + + 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/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/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/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/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/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/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/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/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/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/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_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 new file mode 100644 index 0000000..91b8304 --- /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') + 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' + 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..5d590f1 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/build.gradle @@ -0,0 +1,36 @@ +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..4f90ed4 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + \ 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/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/StructArray2InterfaceServiceAdapter.java b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java new file mode 100644 index 0000000..c92d80c --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapter.java @@ -0,0 +1,585 @@ +//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(); + 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 ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..08fc7af --- /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.i("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.i("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..fce07c2 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + StructArray2InterfaceServiceFactory factory = StructArray2InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for StructArray2InterfaceServiceFactory"); + return StructArray2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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 new file mode 100644 index 0000000..253e540 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapter.java @@ -0,0 +1,586 @@ +//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.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.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 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<>(); + + public StructArrayInterfaceServiceAdapter() + { + } + + public static IStructArrayInterface setService(IStructArrayInterfaceServiceFactory 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(StructArrayInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(StructArrayInterfaceService) 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(); + 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: StructArrayInterfaceService::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(StructArrayInterfaceService) - 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 IStructArrayInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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; + } + 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()); + 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; + + } + // 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); + 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)); + Enum0[] propEnum = mBackendService.getPropEnum(); + + data.putParcelableArray("propEnum", Enum0Parcelable.wrapArray(propEnum)); + 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 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(); + 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 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 "); + } + 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..df5af01 --- /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.i("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.i("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..e66a765 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + StructArrayInterfaceServiceFactory factory = StructArrayInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for StructArrayInterfaceServiceFactory"); + return StructArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..e9cc82e --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/main/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapter.java @@ -0,0 +1,519 @@ +//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.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 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 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<>(); + + public StructInterfaceServiceAdapter() + { + } + + public static IStructInterface setService(IStructInterfaceServiceFactory 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(StructInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(StructInterfaceService) 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(); + 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: StructInterfaceService::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(StructInterfaceService) - 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 IStructInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..c6ded17 --- /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.i("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.i("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..cbce398 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + StructInterfaceServiceFactory factory = StructInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for StructInterfaceServiceFactory"); + return StructInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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/StructArray2InterfaceServiceAdapterTest.java b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java new file mode 100644 index 0000000..ba36d06 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArray2InterfaceServiceAdapterTest.java @@ -0,0 +1,686 @@ +//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.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_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 new file mode 100644 index 0000000..47bdd37 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructArrayInterfaceServiceAdapterTest.java @@ -0,0 +1,730 @@ +//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.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.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_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); + 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()); + 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(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)); + + 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); + + } + + @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); + } + @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); + } + @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); + } + @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); + } + @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 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]; + 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); +} + @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 { + // 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)); + } + + + 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 new file mode 100644 index 0000000..e1b79d6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_android_service/src/test/java/testbed1/testbed1_android_service/StructInterfaceServiceAdapterTest.java @@ -0,0 +1,606 @@ +//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.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.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_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()); + // 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); + } + @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); + } + @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); + } + @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); + } + @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/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 new file mode 100644 index 0000000..0fc122a --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructArrayInterface.java @@ -0,0 +1,107 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructArrayInterfaceEventListener; +import testbed1.testbed1_api.IStructArrayInterface; +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 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 firePropEnumChanged(Enum0[] newValue) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onPropEnumChanged(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); + } + } + + @Override + public void fireSigEnum(Enum0[] paramEnum) { + for (IStructArrayInterfaceEventListener listener : listeners) { + listener.onSigEnum(paramEnum); + } + } + + + 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..945366c --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/AbstractStructInterface.java @@ -0,0 +1,93 @@ +package testbed1.testbed1_api; + +import testbed1.testbed1_api.IStructInterfaceEventListener; +import testbed1.testbed1_api.IStructInterface; +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 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/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 new file mode 100644 index 0000000..7bebca6 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterface.java @@ -0,0 +1,64 @@ +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 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 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); + + void setPropEnum(Enum0[] propEnum); + Enum0[] getPropEnum(); + void firePropEnumChanged(Enum0[] 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); + 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); + 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..a97803f --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructArrayInterfaceEventListener.java @@ -0,0 +1,28 @@ +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 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 new file mode 100644 index 0000000..b962c8d --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterface.java @@ -0,0 +1,57 @@ +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 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 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..a292190 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/IStructInterfaceEventListener.java @@ -0,0 +1,26 @@ +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 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/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/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/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/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/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/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/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 new file mode 100644 index 0000000..5663610 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_api/src/main/java/testbed1/testbed1_api/Testbed1TestHelper.java @@ -0,0 +1,150 @@ +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; + } + + 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 new file mode 100644 index 0000000..49a6606 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "testbed1" +version = "1.0.0" + +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..c09c525 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_client_example/src/main/java/testbed1/testbed1_client_example/Testbed1TestClientApp.java @@ -0,0 +1,351 @@ +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 testbed1.testbed1_api.StructBool; +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 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.i(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.i(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.i(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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new StructInterfaceClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: propBool " + newValue); + } + @Override + public void onPropIntChanged(StructInt newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: propFloat " + newValue); + } + @Override + public void onPropStringChanged(StructString newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSigInt(StructInt paramInt) + { + String text = "Signal sigInt "+ " " + paramInt; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSigFloat(StructFloat paramFloat) + { + String text = "Signal sigFloat "+ " " + paramFloat; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSigString(StructString paramString) + { + String text = "Signal sigString "+ " " + paramString; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/testbed1/testbed1_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..3213a11 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/additions.gradle @@ -0,0 +1,19 @@ + +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/StructArray2InterfaceService.java b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java new file mode 100644 index 0000000..95c7099 --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArray2InterfaceService.java @@ -0,0 +1,271 @@ +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.newSingleThreadExecutor(); + 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 != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) + { + 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 != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) + { + 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 != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) + { + 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 != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) + { + 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 != null && ! m_propEnum.equals(propEnum)) + || (m_propEnum == null && propEnum != null )) + { + 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.i(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.i(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.i(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.i(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.i(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 new file mode 100644 index 0000000..c003d7f --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructArrayInterfaceService.java @@ -0,0 +1,266 @@ +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.Enum0; +import testbed1.testbed1_api.StructBool; +import testbed1.testbed1_api.StructFloat; +import testbed1.testbed1_api.StructInt; +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.newSingleThreadExecutor(); + private StructBool[] m_propBool = new StructBool[]{}; + 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() + { + 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; + } + + + @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 + public StructBool[] funcBool(StructBool[] paramBool) { + Log.i(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.i(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.i(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.i(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 Enum0[] funcEnum(Enum0[] paramEnum) { + Log.i(TAG, "request method funcEnum called, returnig default"); + return new Enum0[]{}; + } + + @Override + public CompletableFuture funcEnumAsync(Enum0[] paramEnum) { + return CompletableFuture.supplyAsync( + () -> {return funcEnum(paramEnum); }, + 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); + } + 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"); + 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); + } + 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 new file mode 100644 index 0000000..825b52d --- /dev/null +++ b/goldenmaster/testbed1/testbed1_impl/src/main/java/testbed1/testbed1_impl/StructInterfaceService.java @@ -0,0 +1,225 @@ +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.StructFloat; +import testbed1.testbed1_api.StructInt; +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.newSingleThreadExecutor(); + 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 != null && ! m_propBool.equals(propBool)) + || (m_propBool == null && propBool != null )) + { + 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 != null && ! m_propInt.equals(propInt)) + || (m_propInt == null && propInt != null )) + { + 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 != null && ! m_propFloat.equals(propFloat)) + || (m_propFloat == null && propFloat != null )) + { + 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 != null && ! m_propString.equals(propString)) + || (m_propString == null && propString != null )) + { + 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.i(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.i(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.i(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.i(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/StructArray2InterfaceJniClient.java b/goldenmaster/testbed1/testbed1jniclient/StructArray2InterfaceJniClient.java new file mode 100644 index 0000000..6eea436 --- /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.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(StructBoolWithArray newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(StructIntWithArray newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropFloatChanged(StructFloatWithArray newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropStringChanged(StructStringWithArray newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onPropEnumChanged(StructEnumWithArray newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropEnumChanged(newValue); + } + @Override + public void onSigBool(StructBoolWithArray paramBool) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(StructIntWithArray paramInt) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigFloat(StructFloatWithArray paramFloat) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigString(StructStringWithArray paramString) + { + Log.i(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 new file mode 100644 index 0000000..068d6d4 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniclient/StructArrayInterfaceJniClient.java @@ -0,0 +1,311 @@ +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.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.StructBool; +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; +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(); + } + + @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 "); + 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 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); + 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 StructArrayInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(StructBool[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(StructInt[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropFloatChanged(StructFloat[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropStringChanged(StructString[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onPropEnumChanged(Enum0[] newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropEnumChanged(newValue); + } + @Override + public void onSigBool(StructBool[] paramBool) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(StructInt[] paramInt) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigFloat(StructFloat[] paramFloat) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigString(StructString[] paramString) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigString "+ " " + paramString); + nativeOnSigString(paramString); + } + @Override + public void onSigEnum(Enum0[] paramEnum) + { + Log.i(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 new file mode 100644 index 0000000..b224af9 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniclient/StructInterfaceJniClient.java @@ -0,0 +1,263 @@ +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_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; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new StructInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onPropBoolChanged(StructBool newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropBoolChanged(newValue); + } + @Override + public void onPropIntChanged(StructInt newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropIntChanged(newValue); + } + @Override + public void onPropFloatChanged(StructFloat newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropFloatChanged(newValue); + } + @Override + public void onPropStringChanged(StructString newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnPropStringChanged(newValue); + } + @Override + public void onSigBool(StructBool paramBool) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigBool "+ " " + paramBool); + nativeOnSigBool(paramBool); + } + @Override + public void onSigInt(StructInt paramInt) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigInt "+ " " + paramInt); + nativeOnSigInt(paramInt); + } + @Override + public void onSigFloat(StructFloat paramFloat) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sigFloat "+ " " + paramFloat); + nativeOnSigFloat(paramFloat); + } + @Override + public void onSigString(StructString paramString) + { + Log.i(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/StructArray2InterfaceJniService.java b/goldenmaster/testbed1/testbed1jniservice/StructArray2InterfaceJniService.java new file mode 100644 index 0000000..ccebe54 --- /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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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.i(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..9f1f0af --- /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.i("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.i("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..df4c587 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + StructArray2InterfaceJniServiceFactory factory = StructArray2InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for StructArray2InterfaceJniServiceFactory"); + return StructArray2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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 new file mode 100644 index 0000000..a9c1dab --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniService.java @@ -0,0 +1,268 @@ +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.Enum0; +import testbed1.testbed1_android_messenger.Enum0Parcelable; +import testbed1.testbed1_api.StructBool; +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; +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.newSingleThreadExecutor(); + + 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(); + } + + + @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 + public StructBool[] funcBool(StructBool[] paramBool) { + Log.i(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.i(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.i(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.i(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 Enum0[] funcEnum(Enum0[] paramEnum) { + Log.i(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 + 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(); + + 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) { + 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 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"); + 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); + } + public void onSigEnum(Enum0[] paramEnum) + { + Log.i(TAG, "onSigEnum, will pass notification to all listeners"); + fireSigEnum(paramEnum); + } + +} diff --git a/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java b/goldenmaster/testbed1/testbed1jniservice/StructArrayInterfaceJniServiceFactory.java new file mode 100644 index 0000000..123bc7f --- /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.i("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.i("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..94549de --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + StructArrayInterfaceJniServiceFactory factory = StructArrayInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for StructArrayInterfaceJniServiceFactory"); + return StructArrayInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..1e57b42 --- /dev/null +++ b/goldenmaster/testbed1/testbed1jniservice/StructInterfaceJniService.java @@ -0,0 +1,224 @@ +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_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; +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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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..626309d --- /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.i("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.i("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..ade42e3 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + StructInterfaceJniServiceFactory factory = StructInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for StructInterfaceJniServiceFactory"); + return StructInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..7062dea --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "testbed1" +version = "1.0.0" + + +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..365fc3d --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + 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..0aa8cfa --- /dev/null +++ b/goldenmaster/testbed1/testbed1serviceexample/src/main/java/testbed1/testbed1serviceexample/Testbed1TestServiceApp.java @@ -0,0 +1,311 @@ +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.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 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.i(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.i(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.i(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.i(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.i(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.i(TAG, "Property from service: propBool " + newValue); + } + @Override + public void onPropIntChanged(StructInt newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: propFloat " + newValue); + } + @Override + public void onPropStringChanged(StructString newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSigInt(StructInt paramInt) + { + String text = "Signal sigInt "+ " " + paramInt; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSigFloat(StructFloat paramFloat) + { + String text = "Signal sigFloat "+ " " + paramFloat; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void onSigString(StructString paramString) + { + String text = "Signal sigString "+ " " + paramString; + outputTextViewSig.setText(text); + Log.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/testbed1/testbed1serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..d5b4fff --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/additions.gradle @@ -0,0 +1,20 @@ +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..9df8d83 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/build.gradle @@ -0,0 +1,36 @@ +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' + testImplementation project(':testbed2_impl') +} 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..9f1fbe2 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/ManyParamInterfaceClient.java @@ -0,0 +1,721 @@ +//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.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.i(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..d75253c --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClient.java @@ -0,0 +1,452 @@ +//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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; + +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.i(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_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()); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + m_prop1 = prop1; + fireProp1Changed(prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called, returning local"); + return m_prop1; + } + + + // 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); + 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..058c278 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClient.java @@ -0,0 +1,470 @@ +//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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; + +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.i(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()); + + + 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()); + + 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()); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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..561030b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/main/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClient.java @@ -0,0 +1,608 @@ +//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.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.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.i(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()); + + + 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()); + + 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()); + + 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()); + 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()); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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 != null && ! m_prop3.equals(prop3)) + || (m_prop3 == null && prop3 != null )) + { + 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 != null && ! m_prop3.equals(prop3)) + || (m_prop3 == null && prop3 != null )) + { + 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..723ce93 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/ManyParamInterfaceClientTest.java @@ -0,0 +1,594 @@ +//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.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; +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); + } + @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); + } + + @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); + } + + @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); + } + + @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..60eb8b4 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct1InterfaceClientTest.java @@ -0,0 +1,359 @@ +//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.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; +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)); + } + @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 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 + 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..6209281 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct2InterfaceClientTest.java @@ -0,0 +1,378 @@ +//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.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; +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)); + } + @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); + } + + @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()); + + 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..ca7e034 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_client/src/test/java/testbed2/testbed2_android_client/NestedStruct3InterfaceClientTest.java @@ -0,0 +1,487 @@ +//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.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; +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)); + } + @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); + } + + @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); + } + + @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()); + + 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()); + + 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/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/NestedStruct1InterfaceMessageType.java b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java new file mode 100644 index 0000000..15bd2a9 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct1InterfaceMessageType.java @@ -0,0 +1,39 @@ +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_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; + + 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/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/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/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/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/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 new file mode 100644 index 0000000..ae7d8f6 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_messenger/src/main/java/testbed2/testbed2_android_messenger/NestedStruct3Parcelable.java @@ -0,0 +1,75 @@ +package testbed2.testbed2_android_messenger; + +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; + + 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(); + 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; + } + + 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 Struct1Parcelable(data.field1), flags); + dest.writeParcelable(new Struct2Parcelable(data.field2), flags); + dest.writeParcelable(new Struct3Parcelable(data.field3), 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..7235574 --- /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') + 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' + 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..a2ca560 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/build.gradle @@ -0,0 +1,36 @@ +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..5db55f7 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapter.java @@ -0,0 +1,527 @@ +//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.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 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<>(); + + public ManyParamInterfaceServiceAdapter() + { + } + + public static IManyParamInterface setService(IManyParamInterfaceServiceFactory 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(ManyParamInterface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(ManyParamInterfaceService) 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(); + 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: ManyParamInterfaceService::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(ManyParamInterfaceService) - 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 IManyParamInterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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..0cb996f --- /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.i("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.i("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..37c5b01 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + ManyParamInterfaceServiceFactory factory = ManyParamInterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for ManyParamInterfaceServiceFactory"); + return ManyParamInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..f0b855f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapter.java @@ -0,0 +1,373 @@ +//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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; + +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 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<>(); + + public NestedStruct1InterfaceServiceAdapter() + { + } + + public static INestedStruct1Interface setService(INestedStruct1InterfaceServiceFactory 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(NestedStruct1Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct1InterfaceService) 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(); + 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: NestedStruct1InterfaceService::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(NestedStruct1InterfaceService) - 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 INestedStruct1InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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_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: { + + 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..b3b1182 --- /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.i("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.i("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..3b8d2bb --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NestedStruct1InterfaceServiceFactory factory = NestedStruct1InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for NestedStruct1InterfaceServiceFactory"); + return NestedStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..4b62c08 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapter.java @@ -0,0 +1,389 @@ +//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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; + +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 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<>(); + + public NestedStruct2InterfaceServiceAdapter() + { + } + + public static INestedStruct2Interface setService(INestedStruct2InterfaceServiceFactory 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(NestedStruct2Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct2InterfaceService) 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(); + 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: NestedStruct2InterfaceService::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(NestedStruct2InterfaceService) - 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 INestedStruct2InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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()); + 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..9d9c565 --- /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.i("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.i("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..f89b087 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NestedStruct2InterfaceServiceFactory factory = NestedStruct2InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for NestedStruct2InterfaceServiceFactory"); + return NestedStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..5a97a71 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/main/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapter.java @@ -0,0 +1,464 @@ +//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.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.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 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<>(); + + public NestedStruct3InterfaceServiceAdapter() + { + } + + public static INestedStruct3Interface setService(INestedStruct3InterfaceServiceFactory 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(NestedStruct3Interface) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate(NestedStruct3InterfaceService) 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(); + 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: NestedStruct3InterfaceService::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(NestedStruct3InterfaceService) - 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 INestedStruct3InterfaceEventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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()); + 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()); + 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..c027313 --- /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.i("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.i("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..7eb4433 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NestedStruct3InterfaceServiceFactory factory = NestedStruct3InterfaceServiceFactory.get(); + Log.i(TAG, "starter: factory set for NestedStruct3InterfaceServiceFactory"); + return NestedStruct3InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..0d2eaf3 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/ManyParamInterfaceServiceAdapterTest.java @@ -0,0 +1,626 @@ +//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.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_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); + } + @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); + } + @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); + } + @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); + } + @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..c0be37a --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct1InterfaceServiceAdapterTest.java @@ -0,0 +1,379 @@ +//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.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_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); + } + @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 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()); + 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..0c04fcf --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct2InterfaceServiceAdapterTest.java @@ -0,0 +1,421 @@ +//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.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_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()); + // 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); + } + @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); + } + @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()); + + 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..f7d382f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_android_service/src/test/java/testbed2/testbed2_android_service/NestedStruct3InterfaceServiceAdapterTest.java @@ -0,0 +1,527 @@ +//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.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_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()); + // 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); + } + @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); + } + @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); + } + @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()); + + 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()); + + 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..53aeb1a --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractManyParamInterface.java @@ -0,0 +1,90 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.IManyParamInterfaceEventListener; +import testbed2.testbed2_api.IManyParamInterface; +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..1706e01 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct1Interface.java @@ -0,0 +1,48 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct1InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct1Interface; +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..846410c --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct2Interface.java @@ -0,0 +1,62 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct2InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct2Interface; +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..cc23432 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/AbstractNestedStruct3Interface.java @@ -0,0 +1,76 @@ +package testbed2.testbed2_api; + +import testbed2.testbed2_api.INestedStruct3InterfaceEventListener; +import testbed2.testbed2_api.INestedStruct3Interface; +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..5c0357e --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/INestedStruct1Interface.java @@ -0,0 +1,37 @@ +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 + 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); + 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..1bdc40f --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/NestedStruct3.java @@ -0,0 +1,58 @@ +package testbed2.testbed2_api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; +import java.util.Arrays; + +public class NestedStruct3 { + + public NestedStruct3(Struct1 field1, Struct2 field2, Struct3 field3) + { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + } + + public NestedStruct3() + { + this.field1 = new Struct1(); + this.field2 = new Struct2(); + this.field3 = new Struct3(); + } + @JsonProperty("field1") + public Struct1 field1; + @JsonProperty("field2") + public Struct2 field2; + @JsonProperty("field3") + public Struct3 field3; + + public NestedStruct3(NestedStruct3 other) + { + this.field1 = new Struct1(other.field1); + this.field2 = new Struct2(other.field2); + this.field3 = new Struct3(other.field3); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NestedStruct3)) return false; + NestedStruct3 other = (NestedStruct3) o; + + return + Objects.equals(this.field1, other.field1) + && Objects.equals(this.field2, other.field2) + && Objects.equals(this.field3, other.field3); + } + + @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); + 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..26c897c --- /dev/null +++ b/goldenmaster/testbed2/testbed2_api/src/main/java/testbed2/testbed2_api/Testbed2TestHelper.java @@ -0,0 +1,107 @@ +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 = Testbed2TestHelper.makeTestStruct1(); + return testStruct; + } + + static public NestedStruct2 makeTestNestedStruct2() + { + NestedStruct2 testStruct = new NestedStruct2(); + testStruct.field1 = Testbed2TestHelper.makeTestStruct1(); + testStruct.field2 = Testbed2TestHelper.makeTestStruct2(); + return testStruct; + } + + static public NestedStruct3 makeTestNestedStruct3() + { + NestedStruct3 testStruct = new NestedStruct3(); + 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 new file mode 100644 index 0000000..3b58c0a --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/build.gradle @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "testbed2" +version = "1.0.0" + +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..9f94901 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_client_example/src/main/java/testbed2/testbed2_client_example/Testbed2TestClientApp.java @@ -0,0 +1,353 @@ +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 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.i(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.i(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.i(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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new ManyParamInterfaceClient(this.getApplicationContext(), ""); + Log.i(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.i(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onProp2Changed(int newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: prop3 " + newValue); + } + @Override + public void onProp4Changed(int newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSig2(int param1, int param2) + { + String text = "Signal sig2 "+ " " + param1+ " " + param2; + outputTextViewSig.setText(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.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.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} \ No newline at end of file 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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/testbed2/testbed2_client_example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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..776b335 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/additions.gradle @@ -0,0 +1,19 @@ + +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..3d2ef78 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/ManyParamInterfaceService.java @@ -0,0 +1,217 @@ +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 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.newSingleThreadExecutor(); + 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.i(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.i(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.i(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.i(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..7c1b7db --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct1InterfaceService.java @@ -0,0 +1,113 @@ +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.NestedStruct1; + + +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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + m_prop1 = prop1; + onProp1Changed(m_prop1); + } + + } + + @Override + public NestedStruct1 getProp1() + { + Log.i(TAG, "request getProp1 called,"); + return m_prop1; + } + + + // methods + + @Override + public void funcNoReturnValue(NestedStruct1 param1) { + Log.i(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.i(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.i(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..0947c36 --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct2InterfaceService.java @@ -0,0 +1,133 @@ +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.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; + + +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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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.i(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.i(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..2b7ad1b --- /dev/null +++ b/goldenmaster/testbed2/testbed2_impl/src/main/java/testbed2/testbed2_impl/NestedStruct3InterfaceService.java @@ -0,0 +1,179 @@ +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.NestedStruct1; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_api.NestedStruct3; + + +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.newSingleThreadExecutor(); + 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 != null && ! m_prop1.equals(prop1)) + || (m_prop1 == null && prop1 != null )) + { + 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 != null && ! m_prop2.equals(prop2)) + || (m_prop2 == null && prop2 != null )) + { + 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 != null && ! m_prop3.equals(prop3)) + || (m_prop3 == null && prop3 != null )) + { + 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.i(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.i(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.i(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..8f710e8 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/ManyParamInterfaceJniClient.java @@ -0,0 +1,255 @@ +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 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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new ManyParamInterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onProp3Changed(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp3Changed(newValue); + } + @Override + public void onProp4Changed(int newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp4Changed(newValue); + } + @Override + public void onSig1(int param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(int param1, int 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.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.i(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..de790ab --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct1InterfaceJniClient.java @@ -0,0 +1,157 @@ +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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +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 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 "); + 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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NestedStruct1InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onSig1(NestedStruct1 param1) + { + Log.i(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 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/testbed2jniclient/NestedStruct2InterfaceJniClient.java b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java new file mode 100644 index 0000000..5e14020 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct2InterfaceJniClient.java @@ -0,0 +1,167 @@ +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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NestedStruct2InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(NestedStruct2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(NestedStruct1 param1, NestedStruct2 param2) + { + Log.i(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..f87fa1c --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniclient/NestedStruct3InterfaceJniClient.java @@ -0,0 +1,215 @@ +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.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 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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new NestedStruct3InterfaceClient(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + @Override + public void onProp1Changed(NestedStruct1 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp1Changed(newValue); + } + @Override + public void onProp2Changed(NestedStruct2 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp2Changed(newValue); + } + @Override + public void onProp3Changed(NestedStruct3 newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOnProp3Changed(newValue); + } + @Override + public void onSig1(NestedStruct1 param1) + { + Log.i(TAG, "NOTIFICATION from messenger client Signal sig1 "+ " " + param1); + nativeOnSig1(param1); + } + @Override + public void onSig2(NestedStruct1 param1, NestedStruct2 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.i(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..14d6fbc --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/ManyParamInterfaceJniService.java @@ -0,0 +1,216 @@ +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 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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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.i(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..10ffd4a --- /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.i("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.i("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..85750d7 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + ManyParamInterfaceJniServiceFactory factory = ManyParamInterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for ManyParamInterfaceJniServiceFactory"); + return ManyParamInterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..fc84bbd --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct1InterfaceJniService.java @@ -0,0 +1,120 @@ +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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; + +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.newSingleThreadExecutor(); + + 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 void funcNoReturnValue(NestedStruct1 param1) { + Log.i(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.i(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.i(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 void nativeFuncNoReturnValue(NestedStruct1 param1); + private native NestedStruct1 nativeFuncNoParams(); + 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..0365996 --- /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.i("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.i("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..4eab1b3 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NestedStruct1InterfaceJniServiceFactory factory = NestedStruct1InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NestedStruct1InterfaceJniServiceFactory"); + return NestedStruct1InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..8a17697 --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct2InterfaceJniService.java @@ -0,0 +1,136 @@ +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.NestedStruct1; +import testbed2.testbed2_android_messenger.NestedStruct1Parcelable; +import testbed2.testbed2_api.NestedStruct2; +import testbed2.testbed2_android_messenger.NestedStruct2Parcelable; + +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.newSingleThreadExecutor(); + + 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.i(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.i(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..acbac93 --- /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.i("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.i("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..b183560 --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NestedStruct2InterfaceJniServiceFactory factory = NestedStruct2InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NestedStruct2InterfaceJniServiceFactory"); + return NestedStruct2InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..f8467aa --- /dev/null +++ b/goldenmaster/testbed2/testbed2jniservice/NestedStruct3InterfaceJniService.java @@ -0,0 +1,180 @@ +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.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 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.newSingleThreadExecutor(); + + 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.i(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.i(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.i(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..da26a69 --- /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.i("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.i("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..4d2f74a --- /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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + NestedStruct3InterfaceJniServiceFactory factory = NestedStruct3InterfaceJniServiceFactory.get(); + Log.i(TAG, "starter: factory set for NestedStruct3InterfaceJniServiceFactory"); + return NestedStruct3InterfaceServiceAdapter.setService(factory); + } + + public static void stop(Context context) + { + if (androidService != null) + { + Log.i(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..a4027e4 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/build.gradle @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "testbed2" +version = "1.0.0" + + +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..af7bff8 --- /dev/null +++ b/goldenmaster/testbed2/testbed2serviceexample/src/main/java/testbed2/testbed2serviceexample/Testbed2TestServiceApp.java @@ -0,0 +1,309 @@ +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.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.i(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.i(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.i(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.i(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.i(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.i(TAG, "Property from service: prop1 " + newValue); + } + @Override + public void onProp2Changed(int newValue) + { + outputTextViewProp.setText("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.i(TAG, "Property from service: prop3 " + newValue); + } + @Override + public void onProp4Changed(int newValue) + { + outputTextViewProp.setText("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.i(TAG, text); + } + @Override + public void onSig2(int param1, int param2) + { + String text = "Signal sig2 "+ " " + param1+ " " + param2; + outputTextViewSig.setText(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.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.i(TAG, text); + } + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(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 0000000..c209e78 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/goldenmaster/testbed2/testbed2serviceexample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 diff --git a/rules.yaml b/rules.yaml index 0f577f2..8877125 100644 --- a/rules.yaml +++ b/rules.yaml @@ -3,7 +3,334 @@ features: - name: api scopes: - match: module - prefix: "{{dot .Module.Name}}.api/" + prefix: "{{camel .Module.Name}}/" documents: - - source: "api/api.java.tpl" - target: "{{Camel .Module.Name}}.java" + - 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: "{{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: + - 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: "{{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: "{{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" + - name: stubs + requires: + - api + scopes: + - match: module + 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: "{{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" + - name: android + requires: + - api + - stubs + scopes: + - match: module + prefix: "{{camel .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: "{{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" + - 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" + - match: interface + 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: "{{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: "{{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" + - 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: + - source: "android/messenger/structparcelable.java.tpl" + target: "{{Camel .Struct.Name }}Parcelable.java" + - match: enum + 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: 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: + - source: "android/client/build.gradle.tpl" + target: "build.gradle" + - source: "android/client/additions.gradle.tpl" + target: "additions.gradle" + - match: interface + 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: "{{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" + - name: jnibridge + requires: + - api + - android + scopes: + - match: interface + prefix: "{{camel .Module.Name}}/{{camel .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" + - match: interface + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}jniclient/" + documents: + - source: "jnibridge/client/jnibridgeclient.java.tpl" + target: "{{Camel .Interface.Name}}JniClient.java" + - name: testclientapp + requires: + - api + - android + scopes: + - match: module + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}_client_example/" + documents: + - source: "testclientapp/build.gradle.tpl" + target: "build.gradle" + - match: module + 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/{{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 + - 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/stringsclient.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 + + - name: testserviceapp + requires: + - api + - android + scopes: + - match: module + prefix: "{{camel .Module.Name}}/{{camel .Module.Name}}serviceexample/" + documents: + - source: "testserviceapp/build.gradle.tpl" + target: "build.gradle" + - match: module + 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/{{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 + - 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 + + - 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/additions.gradle.tpl b/templates/android/client/additions.gradle.tpl new file mode 100644 index 0000000..386dd13 --- /dev/null +++ b/templates/android/client/additions.gradle.tpl @@ -0,0 +1,23 @@ +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') + {{- 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' + 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..08706f0 --- /dev/null +++ b/templates/android/client/build.gradle.tpl @@ -0,0 +1,47 @@ +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 + + 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') + {{- 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' + 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/client.java.tpl b/templates/android/client/client.java.tpl new file mode 100644 index 0000000..6361930 --- /dev/null +++ b/templates/android/client/client.java.tpl @@ -0,0 +1,510 @@ +//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 + +{{- $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.{{.}}; +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 }}; +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; +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; + + +{{- 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 .}} = {{template "getParcelable" . }}.unwrapArray(({{template "getParcelable" . }}[])data.getParcelableArray("{{.Name}}", {{template "getParcelable" . }}.class)); + {{- else }} + {{javaReturn "" .}} {{javaVar .}} = data.getParcelable("{{.Name}}", {{template "getParcelable" . }}.class).get{{Camel .Type}}(); + {{- end }} +{{- end }} + +{{- define "putDataIntoBundle"}} + {{- if and .IsPrimitive }} + data.put{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}", {{ javaVar .}}); + {{- else if .IsArray }} + data.putParcelableArray("{{.Name}}", {{template "getParcelable" . }}.wrapArray({{javaVar .}})); + {{- else }} + data.putParcelable("{{.Name}}", new {{template "getParcelable" . }}({{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 = {{template "getParcelable" .Return }}.unwrapArray(({{template "getParcelable" .Return }}[])bundle.getParcelableArray("result", {{template "getParcelable" .Return }}.class)); + {{- else }} + {{javaReturn "" .Return }} result = bundle.getParcelable("result", {{template "getParcelable" .Return }}.class).get{{Camel .Return.Type}}(); + {{- 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",{{template "getParcelable" .Return }}.wrapArray(result)); + {{- else }} + 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"; + + 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); + } + } + + @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.i(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, {{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; + 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); + } + } + } + + {{- $InterfaceName := Camel .Interface.Name}} + @Override + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + + switch ({{Camel .Interface.Name}}MessageType.fromInteger(msg.what)) + { + case INIT: + { + Bundle data = msg.getData(); + {{template "setClassLoaderIfNeeded" .Interface.Properties}} + {{range .Interface.Properties}} + {{template "getDataFromBundle" . }} + on{{Camel .Name}}({{javaVar .}}); + {{- end}} + + break; + } + {{- range .Interface.Properties }} + case SET_{{Camel .Name}}: + { + Bundle data = msg.getData(); + + {{- if not .IsPrimitive }} + data.setClassLoader({{template "getParcelable" . }}.class.getClassLoader()); + {{- end }} + + {{template "getDataFromBundle" . }} + + on{{Camel .Name}}({{javaVar .}}); + break; + } + {{- end }} + // 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(); + {{template "setClassLoaderIfNeeded" .Params}} + {{- range .Params }} + {{template "getDataFromBundle" . }} + {{- end }} + on{{Camel .Name}}({{javaVars .Params}}); + break; + } + {{- end }} + {{- range .Interface.Operations }} + case RPC_{{Camel .Name}}Resp: { + + Bundle data = msg.getData(); + {{- 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); + 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; + } + + } + }; + +{{- range .Interface.Properties }} + @Override + 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 (or .IsPrimitive (eq .KindType "enum")) (eq .KindType "interface") }} + if (m_{{javaVar .}} != {{javaVar .}}) + {{- else }} + if ( (m_{{javaVar .}} != null && ! m_{{javaVar .}}.equals({{javaVar .}})) + || (m_{{javaVar .}} == null && {{javaVar .}} != null )) + {{- end }} + { + Message msg = new Message(); + msg.what = {{$InterfaceName}}MessageType.PROP_{{Camel .Name}}.getValue(); + Bundle data = new Bundle(); + {{template "putDataIntoBundle" . }} + msg.setData(data); + mClientHandler.sendToService(msg); + } + + } + + 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 (or .IsPrimitive (eq .KindType "enum")) (eq .KindType "interface") }} + if (m_{{javaVar .}} != {{javaVar .}}) + {{- else }} + if ( (m_{{javaVar .}} != null && ! m_{{javaVar .}}.equals({{javaVar .}})) + || (m_{{javaVar .}} == null && {{javaVar .}} != null )) + {{- end }} + { + 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 }} + {{template "putDataIntoBundle" . }} + {{- 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 }} + {{template "getResultFromBundle" . }} + 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..432954b --- /dev/null +++ b/templates/android/client/clienttest.java.tpl @@ -0,0 +1,390 @@ +//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 {{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; +{{- 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 }}; +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; +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); +} + +{{- 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 .}} = {{template "getParcelable" . }}.unwrapArray(({{template "getParcelable" . }}[])data.getParcelableArray("{{.Name}}", {{template "getParcelable" . }}.class)); + {{- else }} + {{javaReturn "" .}} received{{javaVar .}} = data.getParcelable("{{.Name}}", {{template "getParcelable" . }}.class).get{{Camel (.Type)}}(); + {{- end }} +{{- end }} + +{{- define "putTestDataIntoBundle"}} + {{- if .IsPrimitive }} + data.put{{ ( Camel (javaElementType "" .) ) }}{{if .IsArray}}Array{{end}}("{{.Name}}", test{{ javaVar .}}); + {{- else if .IsArray }} + data.putParcelableArray("{{.Name}}", {{template "getParcelable" . }}.wrapArray(test{{javaVar .}})); + {{- else }} + data.putParcelable("{{.Name}}", new {{template "getParcelable" . }}(test{{javaVar .}})); + {{- end }} +{{- end }} + +{{- define "prepareTestValue"}} + {{- if .IsArray }} + {{- if or (.IsPrimitive) (eq .KindType "enum")}} + {{javaType "" .}} test{{ javaVar .}} = new {{javaElementType "" .}}[1]; + test{{ javaVar .}}[0] = {{javaTestValue "" . }}; + {{- else }} + {{javaElementType "" .}}[] test{{ javaVar .}} = new {{javaElementType "" .}}[1]; + {{- if (eq .KindType "extern") }} + test{{ javaVar .}}[0] = {{javaTestValue "" .}}; + {{- else }} + test{{ javaVar .}}[0] = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaTestValue "" .}}{{end}}); + {{- end }} + {{- end}} + {{- else if or (.IsPrimitive) (eq .KindType "enum") }} + {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" . }}; + {{- else if (eq .KindType "extern") }} + {{javaReturn "" . }} test{{ javaVar .}} = {{javaTestValue "" .}}; + {{- else }} + {{javaReturn "" . }} test{{ javaVar .}} = {{template "getMakeTestHelper" . }}({{- if (eq .KindType "interface")}}{{javaTestValue "" .}}{{end}}); + {{- end }} +{{- end }} + + +@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; + InOrder inOrderEventListener; + + 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", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(false); + + 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); + inOrderEventListener = inOrder(listenerMock); + 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", "")); + + inOrderEventListener.verify(listenerMock, times(1)).on_readyStatusChanged(true); + 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}} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} + {{- end }} + + //setup mock expectations + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + {{- range .Interface.Properties}} + {{- 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 }} + @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(); + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + {{- 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}}() + { + {{- template "prepareTestValue" .}} + + 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({{template "getParcelable" . }}.class.getClassLoader()); + {{- end }} + {{template "getReceivedFromBundle" . }} + assertEquals(received{{javaVar .}}, test{{ javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}} + , 1e-6f{{end -}} + ); + } + {{ if or (eq .KindType "extern") (eq .KindType "interface")}} + */ + {{- end }} + {{- end }} +{{- 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 }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} + {{- end }} + + msg.setData(data); + mClientMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + 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}} + + +{{- range .Interface.Operations }} + + + public void on{{.Name}}Request() throws RemoteException { + + // Execute method + {{- range .Params }} + {{- template "prepareTestValue" .}} + {{- end }} + + {{- if not .Return.IsVoid }} + {{- if .Return.IsArray }} + {{- 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]; + {{- if (eq .Return.KindType "extern") }} + expectedResult[0] = {{javaTestValue "" .Return}}; + {{- else }} + expectedResult[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaTestValue "" .Return}}{{end}}); + {{- end }} + {{- end}} + {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} + {{javaReturn "" .Return }} expectedResult = {{javaTestValue "" .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 }} + + AtomicBoolean receivedResp = new AtomicBoolean(false); + {{javaAsyncReturn "" .Return}} resFuture = testedClient.{{camel .Name}}Async({{- range $idx, $p :=.Params }}{{- if $idx}}, {{ end -}}test{{javaVar $p}}{{- end }}); + + resFuture.thenAccept(result -> { + {{- 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() + {{- if or (or (eq .Return.KindType "float") (eq .Return.KindType "float32") ) (eq .Return.KindType "float64") -}} + , 1e-6f{{end -}} + ); + {{- else }} + assertEquals(expectedResult, result); + {{- end }} + {{- 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 data = method_request.getData(); + {{template "setClassLoaderIfNeeded" .Params}} + {{- range .Params }} + {{template "getReceivedFromBundle" . }} + 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); + + //Prepare response + + Message msg = Message.obtain(null, {{$InterfaceName}}MessageType.RPC_{{Camel .Name}}Resp.getValue()); + + 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", {{template "getParcelable" .Return }}.wrapArray(expectedResult)); + {{- else }} + result_data.putParcelable("result", new {{template "getParcelable" .Return }}(expectedResult)); + {{- end }} + {{- end }} + + msg.setData(result_data); + method_request.replyTo.send(msg); + Robolectric.flushForegroundThreadScheduler(); + + assertTrue(receivedResp.get()); + + } + +{{- end}} + +} diff --git a/templates/android/messenger/additions.gradle.tpl b/templates/android/messenger/additions.gradle.tpl new file mode 100644 index 0000000..b186f58 --- /dev/null +++ b/templates/android/messenger/additions.gradle.tpl @@ -0,0 +1,21 @@ +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') + {{- 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' + 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..4dc5073 --- /dev/null +++ b/templates/android/messenger/build.gradle.tpl @@ -0,0 +1,33 @@ +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 + + 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(':{{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' + {{- range .Module.Imports}} + api '{{camel .Name}}:{{camel .Name}}_android_messenger:{{ ($.System.LookupModule .Name).Version }}' + {{- end }} +} diff --git a/templates/android/messenger/enumparcelable.java.tpl b/templates/android/messenger/enumparcelable.java.tpl new file mode 100644 index 0000000..72bdc06 --- /dev/null +++ b/templates/android/messenger/enumparcelable.java.tpl @@ -0,0 +1,69 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; + +import {{camel .Module.Name}}.{{camel .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()); + } + + 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/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/messages.java.tpl b/templates/android/messenger/messages.java.tpl new file mode 100644 index 0000000..a675e59 --- /dev/null +++ b/templates/android/messenger/messages.java.tpl @@ -0,0 +1,49 @@ +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), + INIT(2), + {{- $msgNum := 2}} +{{- 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..67f672f --- /dev/null +++ b/templates/android/messenger/structparcelable.java.tpl @@ -0,0 +1,129 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_messenger; + +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Struct.Name}}; +import android.os.Parcel; +import android.os.Parcelable; + +{{- $typesToImport := getEmptyStringList}} +{{- $interfacesToImport := getEmptyStringList}} +{{- $module := camel .Module.Name}} +{{- range .Struct.Fields }} +{{- 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 { + + public {{Camel .Struct.Name}} data; + + public {{Camel .Struct.Name}}Parcelable({{Camel .Struct.Name}} data) { + this.data = new {{Camel .Struct.Name}}(data); + } + + public {{Camel .Struct.Name}} get{{Camel .Struct.Name}}() + { + return new {{Camel .Struct.Name}}(data); + } + + protected {{Camel .Struct.Name}}Parcelable(Parcel in) { + this.data = new {{Camel .Struct.Name}}(); +{{- range .Struct.Fields }} +{{- 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 }} + +{{- 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 .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}}); +{{- else }} + dest.writeParcelable(new {{javaType "" .}}Parcelable(data.{{camel .Name}}), flags); +{{- end }} +{{- end }} + +{{- end}} + + + } + 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 + public int describeContents() { + return 0; + } + } 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..5480129 --- /dev/null +++ b/templates/android/service/additions.gradle.tpl @@ -0,0 +1,29 @@ +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') + {{- 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' + 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..03c34eb --- /dev/null +++ b/templates/android/service/build.gradle.tpl @@ -0,0 +1,45 @@ +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 + + 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') + {{- 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' + testImplementation 'org.mockito:mockito-inline:5.2.0' +} diff --git a/templates/android/service/implservicefactory.java.tpl b/templates/android/service/implservicefactory.java.tpl new file mode 100644 index 0000000..008709c --- /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 {{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; +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.i("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.i("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..b1ba880 --- /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 {{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; + + +//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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + {{Camel .Interface.Name}}ServiceFactory factory = {{Camel .Interface.Name}}ServiceFactory.get(); + Log.i(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.i(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ No newline at end of file diff --git a/templates/android/service/iservicefactory.java.tpl b/templates/android/service/iservicefactory.java.tpl new file mode 100644 index 0000000..12561ca --- /dev/null +++ b/templates/android/service/iservicefactory.java.tpl @@ -0,0 +1,7 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_android_service; +import {{camel .Module.Name}}.{{camel .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..554e1b2 --- /dev/null +++ b/templates/android/service/serviceadapter.java.tpl @@ -0,0 +1,386 @@ +//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; + +{{- $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.{{.}}; +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; +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; +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 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<>(); + + 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; + } + 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({{Camel .Interface.Name}}) called. For handler " + mHandler); + mBackendService.addEventListener(mHandler); + } + } + return mBackendService; + } + + + @Override + public void onCreate() + { + super.onCreate(); + Log.i(TAG, "LIFECYCLE: onCreate({{Camel .Interface.Name }}Service) 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(); + 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: {{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() + { + Log.i(TAG, "LIFECYCLE: onDestroy({{Camel .Interface.Name }}Service) - 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 I{{Camel .Interface.Name }}EventListener + { + private final ConcurrentHashMap mClients = new ConcurrentHashMap<>(); + + IncomingHandler() + { + super(Looper.getMainLooper()); + } + + 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 + {{- $InterfaceName := Camel .Interface.Name}} + public void handleMessage(Message msg) + { + Log.i(TAG, "Handle msg " + msg); + 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", "")); + sendInit(); + break; + case UNREGISTER_CLIENT: + removeClientActivity(msg.getData().getString("connectionID")); + break; + {{- range .Interface.Properties }} + case PROP_{{Camel .Name}}: + { + Bundle data = msg.getData(); + {{- if not (.IsPrimitive)}} + data.setClassLoader({{template "getParcelable" . }}.class.getClassLoader()); + {{- end}} + {{template "getDataFromBundle" . }} + mBackendService.set{{Camel .Name}}({{javaVar .}}); + 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(); + {{template "setClassLoaderIfNeeded" .Params}} + int callId = data.getInt("callId"); + + {{- range .Params }} + {{template "getDataFromBundle" . }} + {{- 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 }} + {{ template "putResultIntoBundle" . }} + {{- 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 + 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 = {{$InterfaceName}}MessageType.INIT.getValue(); + Bundle data = new Bundle(); + {{range .Interface.Properties}} + {{javaReturn "" .}} {{javaVar .}} = mBackendService.get{{Camel .Name}}(); + {{template "putDataIntoBundle" .}} + {{- end}} + msg.setData(data); + sendMessageToClients(msg); + } + + {{- range .Interface.Properties }} + @Override + 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(); + {{template "putDataIntoBundle" . }} + msg.setData(data); + sendMessageToClients(msg); + } + {{- end }} + {{- range .Interface.Signals }} + @Override + public void on{{Camel .Name}}({{javaParams "" .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 }} + {{template "putDataIntoBundle" . }} + {{- end }} + msg.setData(data); + sendMessageToClients(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/android/service/serviceadaptertest.java.tpl b/templates/android/service/serviceadaptertest.java.tpl new file mode 100644 index 0000000..e2da318 --- /dev/null +++ b/templates/android/service/serviceadaptertest.java.tpl @@ -0,0 +1,399 @@ +//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 {{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; +{{- 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}}_android_service.I{{Camel .Interface.Name}}ServiceFactory; +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; +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 I{{Camel .Interface.Name }}MessageGetter +{ + public void getMessage(Message msg); +} +{{- $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 +{ + + @Mock + private Context mMockContext; + + private {{Camel .Interface.Name }}ServiceAdapter testedServiceAdapter; + private Intent testedServiceAdapterIntent; + private I{{Camel .Interface.Name }}EventListener testedAdapterAsEventListener; + 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; + private String mTestConnectionID1 = "MyTestClient"; + + private I{{Camel .Interface.Name}}ServiceFactory serviceFactory = mock(I{{Camel .Interface.Name}}ServiceFactory.class); + private I{{Camel .Interface.Name }}MessageGetter clientMessagesStorage = 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(); + inOrderBackendService.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) + { + {{- range .Interface.Properties}} + {{- template "prepareInitValue" .}} + when(backendServiceMock.get{{Camel .Name}}()).thenReturn(init{{javaVar .}}); + {{- end }} + + + 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(); + + {{- 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}} + {{ template "getReceivedFromBundle" .}} + {{- end }} + {{template "setClassLoaderIfNeeded" .Interface.Properties}} + {{- range .Interface.Properties}} + {{ if not (or (.IsPrimitive) (eq .KindType "enum")) }}// {{ end -}} + assertEquals(received{{javaVar .}}, init{{ javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}}, + 1e-6f{{end -}} + ); + {{- end }} + + } + + @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, {{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); + 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); + } + + + {{- if not ( or (len (.Interface.Signals)) ( or ( len (.Interface.Operations)) ( len (.Interface.Properties)))) }} + @Test + public void onlySetupAndTeardown() + { + + } + {{- end }} + +{{- range .Interface.Properties }} + {{- if not .IsReadOnly }} + @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(); + + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} + + msg.setData(data); + mServiceMessenger.send(msg); + Robolectric.flushForegroundThreadScheduler(); + 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}}() + { + {{- template "prepareTestValue" .}} + + testedAdapterAsEventListener.on{{Camel .Name}}Changed(test{{javaVar .}}); + Robolectric.flushForegroundThreadScheduler(); + + inOrderClientMessagesHandler.verify(clientMessagesStorage, times(1)).getMessage(messageCaptor.capture()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.SET_{{Camel .Name}}.getValue(), response.what); + Bundle data = response.getData(); + + {{template "getReceivedFromBundle" . }} + + assertEquals(received{{javaVar .}}, test{{javaVar .}} + {{- if and (not .IsArray) (or (or (eq .KindType "float") (eq .KindType "float32") ) (eq .KindType "float64")) -}}, + 1e-6f{{end -}} + ); + } +{{- end}} + +{{- range .Interface.Signals }} + @Test + public void whenNotified{{.Name}}() + { + {{- range .Params }} + {{- template "prepareTestValue" .}} + {{- end }} + + 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()); + Message response = messageCaptor.getValue(); + + assertEquals({{$InterfaceName}}MessageType.SIG_{{Camel .Name}}.getValue(), response.what); + Bundle data = response.getData(); + {{template "setClassLoaderIfNeeded" .Params}} + {{- range .Params }} + {{template "getReceivedFromBundle" . }} + 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}} + + +{{- 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(); + + int callId = 99; + data.putInt("callId", callId); + {{- range .Params }} + {{- template "prepareTestValue" .}} + {{- template "putTestDataIntoBundle" .}} + {{- end}} + + {{- if not .Return.IsVoid }} + {{- if .Return.IsArray }} + {{- 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]; + {{- if (eq .Return.KindType "extern") }} + returnedValue[0] = {{javaTestValue "" .Return}}; + {{- else }} + returnedValue[0] = {{template "getMakeTestHelper" .Return }}({{- if (eq .Return.KindType "interface")}}{{javaTestValue "" .Return}}{{end}}); + {{- end }} + {{- end}} + {{- else if or ( .Return.IsPrimitive) (eq .Return.KindType "enum") }} + {{javaReturn "" .Return }} returnedValue = {{javaTestValue "" .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 }} + + + 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}}({{- 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(); + inOrderClientMessagesHandler.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 (javaElementType "" .Return) ) }}{{if .Return.IsArray}}Array{{end}}("result"{{if not .Return.IsArray}}, {{javaDefault "" .Return}}{{end}}); + {{- else if .Return.IsArray }} + 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({{template "getParcelable" .Return }}.class.getClassLoader()); + {{javaReturn "" .Return }} receivedByClient = resp_data.getParcelable("result", {{template "getParcelable" .Return }}.class).get{{Camel .Return.Type }}(); + {{- end }} + + 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/abstract.java.tpl b/templates/api/abstract.java.tpl new file mode 100644 index 0000000..f775029 --- /dev/null +++ b/templates/api/abstract.java.tpl @@ -0,0 +1,49 @@ +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 }}; + +{{- range .Module.Structs }} +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{camel .Module.Name}}.{{camel .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 }} + + public void fire_readyStatusChanged(boolean isReady) + { + for ({{$interfaceName}}EventListener listener : listeners) { + listener.on_readyStatusChanged(isReady); + } + } + } diff --git a/templates/api/additions.gradle.tpl b/templates/api/additions.gradle.tpl new file mode 100644 index 0000000..9bbb077 --- /dev/null +++ b/templates/api/additions.gradle.tpl @@ -0,0 +1,16 @@ +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_api' +} + +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/api.java.tpl b/templates/api/api.java.tpl deleted file mode 100644 index acd1d3e..0000000 --- a/templates/api/api.java.tpl +++ /dev/null @@ -1,89 +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 .Members }} - @JsonProperty("{{.Value}}") - {{Camel .Name}}, - {{- end }} - } - {{- 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 "" .}} oldValue, {{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 "" .}} oldValue, {{javaType "" .}} newValue); - {{ end }} - // methods - {{- range .Operations }} - {{javaReturn "" .Return}} {{camel .Name}}({{javaParams "" .Params}}); - {{- end }} - - // signal listeners - void 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(IVoidInterfaceEventListener listener) { - listeners.add(listener); - } - public void removeEventListener(IVoidInterfaceEventListener listener) { - listeners.remove(listener); - } - {{- range .Properties }} - @Override - public void fire{{Camel .Name}}Changed({{javaType "" .}} oldValue, {{javaType "" .}} newValue) { - for (IVoidInterfaceEventListener listener : events) { - listener.on{{Camel .Name}}Changed(oldValue, 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/build.gradle.tpl b/templates/api/build.gradle.tpl new file mode 100644 index 0000000..c0264cf --- /dev/null +++ b/templates/api/build.gradle.tpl @@ -0,0 +1,24 @@ +plugins { + id 'java-library' +} + +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +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/enum.java.tpl b/templates/api/enum.java.tpl new file mode 100644 index 0000000..a16a3e8 --- /dev/null +++ b/templates/api/enum.java.tpl @@ -0,0 +1,30 @@ +package {{camel .Module.Name}}.{{camel .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 int value: " + value); + } +} diff --git a/templates/api/eventlistener.java.tpl b/templates/api/eventlistener.java.tpl new file mode 100644 index 0000000..d79df68 --- /dev/null +++ b/templates/api/eventlistener.java.tpl @@ -0,0 +1,18 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; + +{{- range .Module.Structs }} +import {{camel .Module.Name}}.{{camel .Module.Name}}_api.{{Camel .Name}}; +{{- end }} +{{- range .Module.Enums }} +import {{camel .Module.Name}}.{{camel .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 }} + void on_readyStatusChanged(boolean isReady); + } diff --git a/templates/api/interface.java.tpl b/templates/api/interface.java.tpl new file mode 100644 index 0000000..d43605d --- /dev/null +++ b/templates/api/interface.java.tpl @@ -0,0 +1,34 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; + +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}}; +{{- end }} +{{- range .Module.Enums }} +import {{camel .Module.Name}}.{{camel .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 + 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/api/struct.java.tpl b/templates/api/struct.java.tpl new file mode 100644 index 0000000..f3bacda --- /dev/null +++ b/templates/api/struct.java.tpl @@ -0,0 +1,99 @@ +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}} { + + public {{Camel .Struct.Name}}({{javaParams "" .Struct.Fields}}) + { + {{- range .Struct.Fields }} + this.{{camel .Name}} = {{camel .Name}}; + {{- end }} + } + + 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) + { +{{- range .Struct.Fields }} +{{- 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 }} + } + + @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; + } + + +} diff --git a/templates/api/testhelper.java.tpl b/templates/api/testhelper.java.tpl new file mode 100644 index 0000000..c0dbe7b --- /dev/null +++ b/templates/api/testhelper.java.tpl @@ -0,0 +1,68 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_api; + +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 +{ +{{- 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] = {{template "getMakeTestHelper" . }}(); + {{- end }} +{{- else if or (.IsPrimitive) (eq .KindType "enum") }} + testStruct.{{camel .Name}} = {{javaTestValue "" . }}; +{{- else }} + 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/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/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/jnibridge/client/jnibridgeclient.java.tpl b/templates/jnibridge/client/jnibridgeclient.java.tpl new file mode 100644 index 0000000..6a30ece --- /dev/null +++ b/templates/jnibridge/client/jnibridgeclient.java.tpl @@ -0,0 +1,192 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}jniclient; + +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; + +{{- $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.{{.}}; +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; +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); + if (mMessengerClient != null) + { + mMessengerClient.unbindFromService(); + } + } + + private boolean initServiceConnection(Context ctx, String servicePackage, String connectionID) + { + if (mMessengerClient == null) + { + mMessengerClient = new {{Camel .Interface.Name }}Client(ctx, connectionID); + Log.i(TAG, "client created "); + mMessengerClient.addEventListener(this); + } + if (!lastServicePackage.equals(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.i(TAG, "Connection state changed "+isReady); + nativeIsReady(isReady); + } + + //Event listener + {{- range .Interface.Properties }} + @Override + public void on{{Camel .Name}}Changed({{javaType "" .}} newValue) + { + Log.i(TAG, "NOTIFICATION from messenger client " + newValue); + nativeOn{{Camel .Name}}Changed(newValue); + } + {{- end }} + + {{- range .Interface.Signals }} + @Override + public void on{{Camel .Name}}({{javaParams "" .Params}}) + { + Log.i(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); +} diff --git a/templates/jnibridge/service/jnibridgeservice.java.tpl b/templates/jnibridge/service/jnibridgeservice.java.tpl new file mode 100644 index 0000000..6dcbe84 --- /dev/null +++ b/templates/jnibridge/service/jnibridgeservice.java.tpl @@ -0,0 +1,158 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}jniservice; + +import android.os.Messenger; +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; +{{- $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.{{.}}; +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; +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.newSingleThreadExecutor(); + + public {{Camel .Interface.Name}}JniService() + { + fire_readyStatusChanged(true); + } + +{{- 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.i(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..4a84c72 --- /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 {{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; +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.i("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.i("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..f917881 --- /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 {{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; + + +//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.i(TAG, "starter: created intent"); + context.startService(androidService); + Log.i(TAG, "starter: started intent (service) "); + {{Camel .Interface.Name}}JniServiceFactory factory = {{Camel .Interface.Name}}JniServiceFactory.get(); + Log.i(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.i(TAG, "starter: stop the service"); + context.stopService(androidService); + } + androidService = null; + } + +} \ 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..171a37a --- /dev/null +++ b/templates/settings.gradle.tpl @@ -0,0 +1,41 @@ +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() + } +} + +{{- $features := .Features}} + +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 }} +include ':{{camel .Module.Name}}_impl' +{{- end -}} +{{- if .Features.api }} +include ':{{camel .Module.Name}}_api' +{{- end -}} +{{- if .Features.testclientapp }} +include ':{{camel .Module.Name}}_client_example' +{{- end -}} +{{- if .Features.testserviceapp }} +include ':{{camel .Module.Name}}serviceexample' +{{- end -}} diff --git a/templates/stub/additions.gradle.tpl b/templates/stub/additions.gradle.tpl new file mode 100644 index 0000000..a037509 --- /dev/null +++ b/templates/stub/additions.gradle.tpl @@ -0,0 +1,19 @@ + +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..1a96221 --- /dev/null +++ b/templates/stub/build.gradle.tpl @@ -0,0 +1,30 @@ +plugins { + alias(libs.plugins.android.library) +} + +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}_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(':{{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..d125027 --- /dev/null +++ b/templates/stub/implservice.java.tpl @@ -0,0 +1,159 @@ +package {{camel .Module.Name}}.{{camel .Module.Name}}_impl; + +import android.os.Messenger; +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; +{{- $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{{.}}; +import {{$module}}.{{$module}}_impl.{{.}}Service; +{{- 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; +import java.util.Arrays; + + +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.newSingleThreadExecutor(); + + {{- range .Interface.Properties }} + private {{javaReturn "" .}} m_{{javaVar .}} = {{ javaDefault "" . }}; + {{- end}} + + public {{Camel .Interface.Name}}Service() + { + fire_readyStatusChanged(true); + } + +{{- range .Interface.Properties }} + @Override + public void set{{Camel .Name}}({{javaParam "" .}}) + { + Log.i(TAG, "request set{{Camel .Name}} called "); + {{- if .IsArray }} + if (! Arrays.equals(m_{{javaVar .}}, {{javaVar .}})) + {{- else if or (or .IsPrimitive (eq .KindType "enum")) (eq .KindType "interface") }} + if (m_{{javaVar .}} != {{javaVar .}}) + {{- else }} + if ( (m_{{javaVar .}} != null && ! m_{{javaVar .}}.equals({{javaVar .}})) + || (m_{{javaVar .}} == null && {{javaVar .}} != null )) + {{- end}} + { + 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.i(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 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 0000000..c209e78 Binary files /dev/null and b/templates/testappres/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/templates/testappres/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/templates/testappres/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/templates/testappres/res/mipmap-mdpi/ic_launcher_round.webp b/templates/testappres/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/templates/testappres/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/templates/testappres/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/templates/testappres/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/templates/testappres/res/mipmap-xxhdpi/ic_launcher.webp b/templates/testappres/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/templates/testappres/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/templates/testappres/res/mipmap-xxhdpi/ic_launcher_round.webp b/templates/testappres/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/templates/testappres/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/templates/testappres/res/mipmap-xxxhdpi/ic_launcher.webp b/templates/testappres/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/templates/testappres/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/templates/testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp b/templates/testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/templates/testappres/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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/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 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/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..c3c8516 --- /dev/null +++ b/templates/testclientapp/application.java.tpl @@ -0,0 +1,302 @@ +package {{camel .Module.Name}}.{{camel .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 +{{- if len (.Module.Interfaces) }} +{{- $Interface := (index .Module.Interfaces 0) }} + +import {{camel .Module.Name}}.{{camel .Module.Name}}_android_client.{{Camel $Interface.Name }}Client; + +{{- $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.{{.}}; +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; + + + +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 = "{{camel .Module.Name}}.{{camel .Module.Name}}serviceexample.{{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.i(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.i(TAG, "init service connection the client "); + + if (mClient == null) + { + mClient = new {{Camel $Interface.Name }}Client(this.getApplicationContext(), ""); + Log.i(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.i(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.i(TAG, text); + } + {{- end }} + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} +{{- end }} \ No newline at end of file diff --git a/templates/testclientapp/build.gradle.tpl b/templates/testclientapp/build.gradle.tpl new file mode 100644 index 0000000..e310eb0 --- /dev/null +++ b/templates/testclientapp/build.gradle.tpl @@ -0,0 +1,44 @@ +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 + + defaultConfig { + applicationId "{{camel .Module.Name}}.{{camel .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 0000000..c209e78 Binary files /dev/null and b/templates/testclientapp/res/mipmap-hdpi/ic_launcher.webp differ 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 0000000..b2dfe3d Binary files /dev/null and b/templates/testclientapp/res/mipmap-hdpi/ic_launcher_round.webp differ 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 0000000..4f0f1d6 Binary files /dev/null and b/templates/testclientapp/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/templates/testclientapp/res/mipmap-mdpi/ic_launcher_round.webp b/templates/testclientapp/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/templates/testclientapp/res/mipmap-mdpi/ic_launcher_round.webp differ 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 0000000..948a307 Binary files /dev/null and b/templates/testclientapp/res/mipmap-xhdpi/ic_launcher.webp differ 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 0000000..1b9a695 Binary files /dev/null and b/templates/testclientapp/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/templates/testclientapp/res/mipmap-xxhdpi/ic_launcher.webp b/templates/testclientapp/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/templates/testclientapp/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/templates/testclientapp/res/mipmap-xxhdpi/ic_launcher_round.webp b/templates/testclientapp/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/templates/testclientapp/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher.webp b/templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher_round.webp b/templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/templates/testclientapp/res/mipmap-xxxhdpi/ic_launcher_round.webp differ 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 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..b993e29 --- /dev/null +++ b/templates/testserviceapp/application.java.tpl @@ -0,0 +1,273 @@ +package {{camel .Module.Name}}.{{camel .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 +{{- if len (.Module.Interfaces) }} +{{- $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 +{{- $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.{{.}}; +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}}; +import {{camel .Module.Name}}.{{camel .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.i(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.i(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.i(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.i(TAG, text); + } + {{- end }} + @Override + public void on_readyStatusChanged(boolean isReady) + { + if (isReady) + { + Log.i(TAG, "Connected to service "); + } + else + { + Log.i(TAG, "Disconnected from service "); + } + } + +} +{{- end}} diff --git a/templates/testserviceapp/build.gradle.tpl b/templates/testserviceapp/build.gradle.tpl new file mode 100644 index 0000000..733396a --- /dev/null +++ b/templates/testserviceapp/build.gradle.tpl @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.android.application) +} + +group = "{{camel .Module.Name}}" +version = "{{.Module.Version}}" + + +android { + namespace '{{camel .Module.Name}}.{{camel .Module.Name}}serviceexample' + compileSdk 35 + + defaultConfig { + applicationId "{{camel .Module.Name}}.{{camel .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 diff --git a/test-apis b/test-apis index b4edda1..f41c5f3 160000 --- a/test-apis +++ b/test-apis @@ -1 +1 @@ -Subproject commit b4edda143aa66c6d378f4cef1bf825060b4534db +Subproject commit f41c5f39cbb072516d222246ea3c3338f92554e9