How to send data from Android to Flutter properly? - android

I'm trying to send data back and forth from Flutter to my native platform (in this case Android).
In order to keep some model consistency, I have generated the models for all platforms by using Protocol-Buffers.
When I try to pass data from Android to Flutter I'm not finding any way to do it without shenanigans like serializing to a handcrafted JSON.
There must be a way to use protobuf in order to do so, isn't it?
In order to give context, I have made a minimal app to try to solve this problem:
My Protocol Buffer
syntax = "proto3";
option java_package = "com.test.protobuf_test";
option java_outer_classname = "ProtoModel";
message SimplePerson {
int32 id= 1;
string name= 2;
}
From which I generate my model using:
protoc --java_out and protoc --dart_out
In Dart I get my class
class SimplePerson extends $pb.GeneratedMessage {...}
And in Java
public final class ProtoModel {
...
public static final class SimplePerson extends
com.google.protobuf.GeneratedMessageV3 implements
SimplePersonOrBuilder {...}
}
From Android inside my method channel, I am trying to pass one or many ProtoModel.SimplePerson objects back to Dart.
No success so far.
How would you actually do it?
I'd expect it to be something like
In Java:
ProtoModel.SimplePerson person = ProtoModel.SimplePerson.newBuilder().setId(3).setName("Person Name").build();
result(person);
And in Dart:
var result = await platform.invokeMethod("generatePerson");
if(result is SimplePerson) {
print("Success!");
} else {
print("Failure!");
}
So far I'm only getting Failures or Exceptions.
Thanks!

your very close your using result but i have it working with result.success
when (call.method) {
"getPlatformVersion" -> result.success(getPlatformVersion().toByteArray())
}
private fun getPlatformVersion(): Models.Version {
return Models.Version.newBuilder().setVersionName("Android ${android.os.Build.VERSION.RELEASE}").build()
}
great example here https://www.freecodecamp.org/news/flutter-platform-channels-with-protobuf-e895e533dfb7/
EDIT didnt see how old this post was
I have to use this as Pigeon is sill early access, and although pigeon was generally harder to set up i do prefer it

Related

Extent Report Issue Parallel testing

I have the following Reporting code:
public class Reporting {
private ExtentHtmlReporter extentHtmlReporter;
private static ThreadLocal<ExtentReports> extentReports = new ThreadLocal<>();
private static ThreadLocal<ExtentTest> extentTest = new ThreadLocal<>();
public synchronized ExtentTest createInstanceReport(String testCaseName) {
System.out.println(extentReports.get());
new File(Constants.userDir + "/Reports/").mkdirs();
// To generate report with name
extentHtmlReporter = new ExtentHtmlReporter(
Constants.userDir + "/Reports/" +
"ExecutionReport_" + new SimpleDateFormat(
Constants.date).format(new Date()) + ".html");
// Setting Document Title
extentHtmlReporter.config().setDocumentTitle("Demo");
// Setting Report Name
extentHtmlReporter.config().setReportName("Demo Automation");
// Setting Theme
extentHtmlReporter.config().setTheme(Theme.STANDARD);
// Setting Chart location
extentHtmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
// Setting Chart visibility
extentHtmlReporter.config().setChartVisibilityOnOpen(false);
// Setting Time stamp
extentHtmlReporter.config().setTimeStampFormat("yyyy-MM-dd HH:mm:ss");
// Setting append exist as true
extentHtmlReporter.setAppendExisting(true);
ExtentReports extentReports = new ExtentReports();
extentReports.attachReporter(extentHtmlReporter);
// Setting system info
extentReports.setSystemInfo("Name",
BaseTest.prop.getProperty(Constants.testerName));
extentReports.setSystemInfo("Environment",
BaseTest.prop.getProperty(Constants.environment));
extentReports.setSystemInfo("Browser",
BaseTest.prop.getProperty(Constants.browser));
Reporting.extentReports.set(extentReports); // Instead of using here extentReport thread like this, Can anyone suggest to use it directly
// Add test case name in report
ExtentTest extentTest = Reporting.extentTest.get();
extentTest = Reporting.extentReports.get().createTest(testCaseName);
Reporting.extentTest.set(extentTest);
// Assigning categories
extentTest.assignCategory(MultiFunction.getProp()
.getProperty(Constants.browser));
System.out.println(Reporting.extentReports.get());
System.out.println(Reporting.extentTest.get());
return extentTest;
}
public synchronized ExtentTest getExtentTest() {
return extentTest.get();
}
public synchronized ExtentReports getInstanceReport() {
return extentReports.get();
}
public synchronized void remove() {
extentReports.remove();
extentTest.remove();
}
}
I was trying parallel testing using TestNG (and will have to use Selenium grid and sauce in future). I execute 2 test cases then only one test case result is added in the report.
I have isolated the extentTest, extentReporter and WebDriver instances using threadPool.
Tried below with extentHtmlReporter instance:
1) Tried to make it static(no luck)
2) Tried to make it local (the same behaviour, getting only 1 test case result)
3) Tried as a non-static global variable ( no luck)
Could you suggest how to solve the above issue?
Please note: Only one report is generated. But when I tried to run parallel test cases in debug mode reports are generated for both the test case. I think because one test case gets over its killing some instance (when running in non-debug mode)
Also, I want to redesign the following place in my code:
For extentRpeort, I am using:
Reporting.extentReports.set(extentReports);
To add extentReport instance to my extentReport Thread.
Instead of adding like this I want to use it directly so as to reduce line of code.
If I understand correctly you have to generate Report from all executed TestNG cases.
However, from code which you shared, it is very visible that you will have some trouble with it. You are making a few critical mistakes and result are obvious:
For generating reports with TestNG I will suggest grabbing information about test execution from TestNG listener. Something like:
public final class TestNGListener extends TestListenerAdapter implements IInvokedMethodListener, ISuiteListener {
#Override
public void onStart(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_STARTED, context.getName()));
}
#Override
public void onFinish(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_FINISHED, context.getName()));
}
#Override
public void onTestStart(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_STARTED, getMethodName(result)));
}
#Override
public void onTestSuccess(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_SUCCESS, getMethodName(result)));
processTestResult(result);
}
#Override
public void onTestFailure(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_FAILED, getMethodName(result)));
}
You can't do everything in one method! You broke Single Responsibility Principle. Your createInstanceReport() is doing all jobs (setting report details, set system info, attach an executed test case to report) at one place. You have to redesign this logic to some logical separate operations. After redesigning your problem with the next line:
Reporting.extentReports.set(extentReports)
Could successfully disappear.
You have to consider a case, why you need to use exactly Extent, Reports Version 3. TestNG has test reports from the box. They are poor but they are presented out of the box. If you want just to improve it a little bit you could use ReportNG over TestNG.
It is quite easy to configure: Configuring ReportNG with TestNG for HTML Reports.
It isn't maintained, but it makes TestNG reports really eye candy and understandable.
Anyway, my suggestion is to use TestNGListener for getting info about test cases execution. And read more about good programming practice.
Work with TestNG/jUnit (or other runner framework that you are using) listener, here is a good example how to do it.
Do not put everything in a single class.
https://www.swtestacademy.com/extent-reports-version-3-reporting-testng/
The issue was with the flushing of extent report instance.
I was using ThreadLocal for storing extent report instance and was flushing the wrong instance.

Where to put appending of required data (to be passed on API) globally?

Clean Architecture Question
I have many form activities that has 1 common data that must be appended during submission, my question is, where will the logic must be placed?
Domain or Presentation Layer?
For Presentation:
I'll create a BaseFormActivity that has a method of inserting the needed data on a form that is child of BaseForm which contains the needed data globally.
BaseForm: (to be extended by all forms)
public class BaseForm {
private String globalData;
//getter setters...
}
BaseFormPresenter:
public class BaseFormPresenter extends BaseFormMvpView {
private final GetGlobalDataInteractor mGetData; //to be injected, this is a use case
public void getGlobalData() {
mGetData.execute()
.subscribe(data -> {
getMvpView().showGlobalData(data);
}); //just for the sake of simplicity
}
}
BaseFormActivity: (which is extended by all activity that handles form)
public abstract class BaseFormActivity implements BaseFormMvpView {
#Inject
BaseFormPresenter mPresenter;
//onCreate(), etc
}
SpecificFormActivity: (extends BaseFormActivity)
public class SpecificFormActivity extends BaseFormActivity {
private SpecificForm mForm; //extends BaseForm
//onCreate(), etc
#Override
public void showGlobalData(String data) {
mForm.setGlobalData(data);
}
//then ill just call the presenter to get the global data before submitting
}
For Domain:
SubmitSpecificFormInteractor: (Sorry for the coding, it is just a representation on what I'm thinking to do)
public class SubmitSpecificFormInteractor extends SingleUseCase<Return, Param> {
//to be injected
GlobalRepository mGlobalRepository;
SpecificFormRepository mFormRepository;
//some initialization
public Single<SomeResponse> buildObservable(#NonNull String specificFormData, String anotherSpecificFormData) {
return mGlobalRepository.getGlobalData()
.map(globalData -> SpecificFormDto.create(
specificFormData, anotherSpecificFormData, globalData)) //create the dto then append global data
.flatMap(specificFormDto -> mFormRepository.submit(specificFormDto)) //then submit data
}
}
I'm thinking of placing it on the domain layer (you can see that it is much isolated, but I'll have to do it on all form submissions, which is redundancy), but still I just want to make my decision solid. Refactoring is time consuming. Hope you understand my point here, specially on my pseudocode-like coding. Feel free to comment if there's something hard to understand. Thank you.
In Clean Architecture all business rules go to use case interactors. The main goal is to keep the business rules independent from any details - from any framework - that includes android as well.
The Clean Architecture then uses "interface adapters" (in UI part called "controllers" and "presenters") to map between data most convenient for the inner circles and data convenient for the frameworks.
So in ur case u should go for ur second proposal - even if that means that u have to call the interactor from multiple places and have to map some data types. it is worth the benefits (business rules free from details).
For a more detailed discussion about use case interactors, controllers and presenters pls refer to my posts here: https://plainionist.github.io/Implementing-Clean-Architecture-UseCases/
and here https://plainionist.github.io/Implementing-Clean-Architecture-Controller-Presenter/

How do i mock REST backend in an android app

My android app communicate with backend service through REST API . I want to mock out this API to quickly develop the front end.
I am using android volley as client side networking library.
You can use the dependency injection design pattern for this.
Basically you specify an interface that defines a set of methods corresponding to the queries you have in your REST backend, e.g.:
interface DataSupplier {
// Lookup user by ID
User getUser(int id);
// Get all blog posts posted by a specific user.
List<BlogPost> getUsersBlogPosts(int userId);
}
Now in the class where you need to query the backend, you specify an injector. This can be done in multiple ways (e.g. constructor injection, setter injection - see the wiki article for more details). An injector lets you inject an implementation of the dependency into the class that depends on it. Let us assume you use constructor injection. Your class that uses the backend would look like this:
public class DependentClass {
private final DataSupplier mSupplier;
public DependentClass(DataSupplier dataSupplier) {
mSupplier = dataSupplier;
}
// Now you simply call mSupplier whenever you need to query the mock
// (or - later in development - the real) REST service, e.g.:
public void printUserName() {
System.out.println("User name: " + mSupplier.getUser(42).getName());
}
}
Then you create a mock implementation of DataSupplier:
public class MockRestService implements DataSupplier {
#Override
public User getUser(int id) {
// Return a dummy user that matches the given ID
// with 'Alice' as the username.
return new User(id, "Alice");
}
#Override
public List<BlogPost> getUsersBlogPosts(int userId) {
List<BlogPost> result = new ArrayList<BlogPost>();
result.add(new BlogPost("Some Title", "Some body text"));
result.add(new BlogPost("Another Title", "Another body text"));
result.add(new BlogPost("A Third Title", "A third body text"));
return result;
}
}
and use that to instantiate your dependent class:
DepedentClass restClient = new DepedentClass(new MockRestService());
Now you can use restClient as if it was connected to your actual backend. It will simply return dummy objects that you can use to develop your front end.
When you are done with your front end and ready to implement your backend, you do so by creating another implementation of DataSupplier that sets up a connection to your REST backend and queries it for real objects. Let us say you name this implementation RestService. Now you can simply replace the constructor creating the MockRestService with your RestService constructor like so:
DepedentClass restClient = new DepedentClass(new RestService());
And there you have it: by swapping a single constructor call, you can change your front end code from using dummy objects to using real REST-delivered objects.
You could even have a debug flag and create the restClient according to the state of your application (debug or release):
boolean debug = true;
DependentClass restClient = null;
if (debug) {
restClient = new DepedentClass(new MockRestService());
} else {
restClient = new DepedentClass(new RestService());
}
I've recently created RESTMock. It is a library for Mocking REST API's in android tests. It can be used during development though. You would need to set it up following the README on github and create a basic Android Instrumentation test that would start your app and do nothing. This way the app is started with the Mock Server in background.
Example test:
public class SmokeTest {
#Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<MainActivity>(
SplashActivity.class,
true,
false);
#Test
public void smokeTest() throws InterruptedException {
rule.launchActivity(null);
Thread.sleep(10000000);
}
}

Using internal android classes

I want to use an android internal class, com.android.internal.widget.LockPatternUtils .
I searched for examples and I got this:
LockPatternUtils lpu = new LockPatternUtils(this.getContentResolver());
However, i cant import, autimport does not appear and manually doing
import com.android.internal.widget.LockPatternUtils
doesnt work.
How is this handled? I think there was something about creating an interface but not sure about it
EDIT: I got this:
boolean patternLocked = android.provider.Settings.System.getInt(
getContentResolver(),Settings.System.LOCK_PATTERN_ENABLED, 0)==1;
That works for the pattern lock, but i cant know about the pin or password lock
Use reflection to access the internal Android method getActivePasswordQuality (line 350 of LockPatternUtils.java) and compare the int it returns to the int for any of the DevicePolicyManager Constants:
protected boolean isSecure() {
String LOCK_PATTERN_UTILS = "com.android.internal.widget.LockPatternUtils";
try {
Class<?> lockPatternUtilsClass = Class.forName(LOCK_PATTERN_UTILS);
Object lockPatternUtils = lockPatternUtilsClass.getConstructor(Context.class).newInstance(this);
Method method = lockPatternUtilsClass.getMethod("getActivePasswordQuality");
int lockProtectionLevel = Integer.valueOf(String.valueOf(method.invoke(lockPatternUtils)));
// Then check if lockProtectionLevel == DevicePolicyManager.TheConstantForWhicheverLevelOfProtectionYouWantToEnforce, and return true if the check passes, false if it fails
}
catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
How is this handled?
Those classes are only available as part of full firmware builds, not from Android SDK apps.
You can not use Android internal classes, they do not come with the public SDK.
You can build your own SDK exposing them more or less as they do here How do I build the Android SDK with hidden and internal APIs available?
This is not recommended because you will have no guarantees.

Is it safe to write Android compatible code this way?

is it safe to write such compatible code on Android?
if (Build.os.SDK_INT >= 11) {
newClass instance = new newClass();
....
}
else {
oldClass instance = new oldClass();
....
}
one of my colleagues argue with me that ClassNotFoundException might be thrown up when running the above code since ClassLoader is attempting to load newClass on an android os device which is below android 11. But I've tried couple times, and didn't see this happen.
After googling around for couple hours, I didn't find any information on how and when android default classLoader loads a specific class.
You should check the compatability like the following... It gives you more accurate than the above:
private static int currentApi = 0;
public static int getApiLevel() {
if (currentApi > 0) {
return currentApi;
}
if (android.os.Build.VERSION.SDK.equalsIgnoreCase("3")) {
currentApi = 3;
} else {
try {
Field f = android.os.Build.VERSION.class.getDeclaredField("SDK_INT");
currentApi = (Integer) f.get(null);
} catch (Exception e) {
return 0;
}
}
return currentApi;
}
you can alway use reflection to check if the class exists:
try {
Class.forName("yourclass")
} catch (ClassNotFoundExecption) {
oldClass instance = new oldClass();
}
Yes, this is safe to do on recent versions of Android. I want to say froyo and above, but it may have been even earlier than that. I don't recall for sure.
What happens is that dalvik performs a verification pass on the dex file at install time. For any classes/methods/fields that it can't resolve, it replaces those accesses with an instruction that throws a VerifyError.
In your example, if that code got loaded on, e.g. api 10, newClass instance = new newClass() would conceptually be replaced with throw new VerifYError(). So as long as that branch never gets executed at runtime, everything is good.
Short answer - don't do it.
Most VMs only load a class when it is absolutely needed. However a class loader is allowed to cache binary representation of classes beforehand[1].
Class loaders are allowed to cache binary representations of types,
load types early in anticipation of eventual use, or load types
together in related groups.
[1] - http://www.artima.com/insidejvm/ed2/lifetype2.html
[2] - http://developer.android.com/tools/extras/support-library.html
Edit - Have you checked if the class you need is available as part of the android support package ? [2]

Categories

Resources