diff options
author | Ang Li <angli@google.com> | 2018-01-12 11:54:03 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-12 11:54:03 -0800 |
commit | c2d947efa173968571cbcc11376159647a9ff356 (patch) | |
tree | c8e06f22c7dffb5ffed4f7824120fb0dff8a5354 /examples | |
parent | 4d02684ade1a312b39591a94a92328dc0b65b883 (diff) | |
download | mobly-snippet-lib-c2d947efa173968571cbcc11376159647a9ff356.tar.gz |
Support custom converters of non-primitive types. (#86)
Now users can supply custom logic for object serialization/de-serialization
through a centralized class, and snippet lib will automatically use the custom converters.
Added a new example to demonstrate this feature.
Diffstat (limited to 'examples')
8 files changed, 349 insertions, 0 deletions
diff --git a/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet.java b/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet.java new file mode 100644 index 0000000..6f5af65 --- /dev/null +++ b/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.android.mobly.snippet.example6; + +import com.google.android.mobly.snippet.Snippet; +import com.google.android.mobly.snippet.rpc.Rpc; + +public class ExampleSnippet implements Snippet { + @Rpc(description = "Returns the given integer with the prefix \"foo\"") + public String getFoo(Integer input) { + return "foo " + input; + } + + @Override + public void shutdown() {} +} diff --git a/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet2.java b/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet2.java new file mode 100644 index 0000000..10a259e --- /dev/null +++ b/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet2.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.android.mobly.snippet.example6; + +import com.google.android.mobly.snippet.Snippet; +import com.google.android.mobly.snippet.rpc.Rpc; + +import com.google.android.mobly.snippet.rpc.RunOnUiThread; +import java.io.IOException; + +public class ExampleSnippet2 implements Snippet { + @Rpc(description = "Returns the given string with the prefix \"bar\"") + public String getBar(String input) { + return "bar " + input; + } + + @Rpc(description = "Throws an exception") + public String throwSomething() throws IOException { + throw new IOException("Example exception from throwSomething()"); + } + + @Rpc(description = "Throws an exception from the main thread") + // @RunOnUiThread makes this method execute on the main thread, but only has effect when + // invoked as an RPC. It does not affect how this method executes if invoked directly in Java. + // This annotation can also be applied to the constructor and the shutdown() method. + @RunOnUiThread + public String throwSomethingFromMainThread() throws IOException { + throw new IOException("Example exception from throwSomethingFromMainThread()"); + } + + @Override + public void shutdown() {} +} diff --git a/examples/ex6_complex_type_conversion/README.md b/examples/ex6_complex_type_conversion/README.md new file mode 100644 index 0000000..70d0289 --- /dev/null +++ b/examples/ex6_complex_type_conversion/README.md @@ -0,0 +1,108 @@ +# Complex Type Conversion in Snippet Example + +This tutorial shows you how to use a custom object in Snippet Lib. + +This example assumes basic familiarity with Snippet Lib as demonstrated in +[Example 1](../ex1_standalone_app/README.md). + +## Tutorial + +1. Use Android Studio to create a new app project, similar to + [Example 1](../ex1_standalone_app/README.md). + +1. Create a complex type in Java: + ```java + public class CustomType { + private String myValue; + CustomType(String value) { + myValue = value; + } + + String getMyValue() { + return myValue; + } + public void setMyValue(String newValue) { + myValue = newValue; + } + } + ``` +1. Create a Java class implementing `SnippetObjectConverter`, which defines how the complex type + should be converted against `JSONObject`: + ```java + public class ExampleObjectConverter implements SnippetObjectConverter { + @Override + public JSONObject serialize(Object object) throws JSONException { + JSONObject result = new JSONObject(); + if (object instanceof CustomType) { + CustomType input = (CustomType) object; + result.put("Value", input.getMyValue()); + return result; + } + return null; + } + + @Override + public Object deserialize(JSONObject jsonObject, Type type) throws JSONException { + if (type == CustomType.class) { + return new CustomType(jsonObject.getString("Value")); + } + return null; + } + } + ``` +1. Write a Java class implementing `Snippet` and add Rpc methods that takes your complex type as + a parameter and another Rpc method that returns the complext type directly. + + ```java + package com.my.app; + ... + public class ExampleSnippet implements Snippet { + @Rpc(description = "Pass a complex type as a snippet parameter.") + public String passComplexTypeToSnippet(CustomType input) { + Log.i("Old value is: " + input.getMyValue()); + return "The value in CustomType is: " + input.getMyValue(); + } + + @Rpc(description = "Returns a complex type from snippet.") + public CustomType returnComplexTypeFromSnippet(String value) { + return new CustomType(value); + } + @Override + public void shutdown() {} + } + ``` + +1. In `AndroidManifest.xml`, specify the converter class as a `meta-data` named + `mobly-object-converter` + + ```xml + <manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.my.app"> + <application> + <meta-data + android:name="mobly-object-converter" + android:value="com.my.app.ExampleObjectConverter" /> + ... + ``` + +## Running the example code + +This folder contains a fully working example of a standalone snippet apk. + +1. Compile the example + + ./gradlew examples:ex6_complex_type_conversion:assembleDebug + +1. Install the apk on your phone + + adb install -r ./examples/ex6_complex_type_conversion/build/outputs/apk/ex6_complex_type_conversion-debug.apk + +1. Use Mobly's `snippet_shell` from mobly to trigger the Rpc methods: + + snippet_shell.py com.google.android.mobly.snippet.example6 + + >>> s.passComplexTypeToSnippet({'Value': 'Hello'}) + 'The value in CustomType is: Hello' + >>> s.returnComplexTypeFromSnippet('Bye') + {'Value': 'Bye'} diff --git a/examples/ex6_complex_type_conversion/build.gradle b/examples/ex6_complex_type_conversion/build.gradle new file mode 100644 index 0000000..40d45bc --- /dev/null +++ b/examples/ex6_complex_type_conversion/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion '26.0.2' + + defaultConfig { + applicationId "com.google.android.mobly.snippet.example6" + minSdkVersion 16 + targetSdkVersion 26 + versionCode 1 + versionName "0.0.1" + } + lintOptions { + abortOnError true + checkAllWarnings true + warningsAsErrors true + } +} + +dependencies { + // The 'implementation project' dep is to compile against the snippet lib source in + // this repo. For your own snippets, you'll want to use the regular + // 'implementation' dep instead: + //implementation 'com.google.android.mobly:mobly-snippet-lib:1.2.0' + implementation project(':mobly-snippet-lib') +} diff --git a/examples/ex6_complex_type_conversion/src/main/AndroidManifest.xml b/examples/ex6_complex_type_conversion/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c1548e7 --- /dev/null +++ b/examples/ex6_complex_type_conversion/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.mobly.snippet.example6"> + + <application> + <!-- Required: list of all classes with @Rpc methods. --> + <meta-data + android:name="mobly-snippets" + android:value="com.google.android.mobly.snippet.example6.ExampleSnippet" /> + <!-- Optional: a class used for converting Java objects to/from JSON. --> + <meta-data + android:name="mobly-object-converter" + android:value="com.google.android.mobly.snippet.example6.ExampleObjectConverter" /> + <meta-data + android:name="mobly-log-tag" + android:value="MoblySnippetLibExample6" /> + </application> + + <instrumentation + android:name="com.google.android.mobly.snippet.SnippetRunner" + android:targetPackage="com.google.android.mobly.snippet.example6" /> +</manifest> diff --git a/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/CustomType.java b/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/CustomType.java new file mode 100644 index 0000000..223b63e --- /dev/null +++ b/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/CustomType.java @@ -0,0 +1,21 @@ +package com.google.android.mobly.snippet.example6; + +/** + * A data class that defines a non-primitive type. + * + * This type is used to demonstrate serialization and de-serialization of complex type objects in + * Mobly Snippet Lib for Android. + */ +public class CustomType { + private String myValue; + CustomType(String value) { + myValue = value; + } + + String getMyValue() { + return myValue; + } + public void setMyValue(String newValue) { + myValue = newValue; + } +} diff --git a/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/ExampleObjectConverter.java b/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/ExampleObjectConverter.java new file mode 100644 index 0000000..5fc4f22 --- /dev/null +++ b/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/ExampleObjectConverter.java @@ -0,0 +1,36 @@ +package com.google.android.mobly.snippet.example6; + +import android.os.Bundle; + +import com.google.android.mobly.snippet.SnippetObjectConverter; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Type; + + +/** + * Example showing how to supply custom object converter to Mobly Snippet Lib. + */ + +public class ExampleObjectConverter implements SnippetObjectConverter { + @Override + public JSONObject serialize(Object object) throws JSONException { + JSONObject result = new JSONObject(); + if (object instanceof CustomType) { + CustomType input = (CustomType) object; + result.put("Value", input.getMyValue()); + return result; + } + return null; + } + + @Override + public Object deserialize(JSONObject jsonObject, Type type) throws JSONException { + if (type == CustomType.class) { + return new CustomType(jsonObject.getString("Value")); + } + return null; + } +} diff --git a/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet.java b/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet.java new file mode 100644 index 0000000..3306f85 --- /dev/null +++ b/examples/ex6_complex_type_conversion/src/main/java/com/google/android/mobly/snippet/example6/ExampleSnippet.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.android.mobly.snippet.example6; + +import com.google.android.mobly.snippet.Snippet; +import com.google.android.mobly.snippet.rpc.Rpc; +import com.google.android.mobly.snippet.util.Log; + +import java.util.ArrayList; + +/** + * Example snippet showing converting complex type objects using custom logic. + * + * For complex types in Java, one can supply a custom object converter to Snippet Lib to specify how + * each complex type should be serialized/de-serialized. With this, users don't have to explicitly + * call a serializer or de-serializer in every single Rpc method, which simplifies code. + */ +public class ExampleSnippet implements Snippet { + @Rpc(description = "Pass a complex type as a snippet parameter.") + public String passComplexTypeToSnippet(CustomType input) { + Log.i("Old value is: " + input.getMyValue()); + return "The value in CustomType is: " + input.getMyValue(); + } + + @Rpc(description = "Returns a complex type from snippet.") + public CustomType returnComplexTypeFromSnippet(String value) { + return new CustomType(value); + } + + /** + * Demonstrates serialization/de-serialization of a collection of custom type objects. + */ + @Rpc(description = "Update values for multiple CustomType objects.") + public ArrayList<CustomType> updateValues(ArrayList<CustomType> objects, String newValue) { + for (CustomType obj : objects) { + obj.setMyValue(newValue); + } + return objects; + } + + @Override + public void shutdown() {} +} |