I wrote a Cordova plugin for Android to build a phonegap app with an HTML5 GUI.
I now want to have a native interface too and was wondering what is the neatest option to reuse my plugin for the native UI. Basically I would like to have two apps, one with a phonegap (HTML5) interface and one with a native Android interface, both of them using the Cordova plugin.
The plugin extends the CordovaPlugin class, so for this reason I don't know how to use it without calling the following method from the javascript in the WebView, as described here http://docs.phonegap.com/en/2.3.0/guide_plugin-development_android_index.md.html
exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);
I just want to call the native side of the plugin without going through a WebView:
#Override
public boolean execute(String action, JSONArray args,
CallbackContext callbackContext) throws JSONException { ... }
Provided that I could just adapt the code from the plugin fairly easily, I would like to find a method by which the plugin remains exactly the same for better decoupling of frontend/backend (I could change the code in one app without the need to replicate it in the other app).
Is this possible at all? I understand this is not the point of having a Cordova plugin, but I would like to find a way around it.
Thanks.
In my opinion you need to apply Facade pattern:
http://en.wikipedia.org/wiki/Facade_pattern
Simply extract your business logic from Cordova plugin to dedicated class called MyFacade and hide all your business logic behind.
The other way is to do something like this:
MyCordovaPlugin myPlugin = new MyCordovaPlugin();
myPlugin.execute("foo", new JSONArray(), new MyCallbackContext() {
#override
public void handlePluginResult(PluginResult pluginResult) {
//your code for handling plugin result for Android UI
}
}
Where MyCallbackContext implementation is:
public abstract class MyCallbackContext extends CallbackContext {
public MyCallbackContext() {
super(null, null);
}
public void sendPluginResult(PluginResult pluginResult) {
synchronized (this) {
if (finished) {
Log.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage());
return;
} else {
finished = !pluginResult.getKeepCallback();
}
}
handlePluginResult(pluginResult);
}
public abstract void handlePluginResult(PluginResult pluginResult);
}
This second way works only with current version of Cordova and is based on this source codes:
https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/api/CordovaPlugin.java
https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/api/CallbackContext.java
Related
I am rewriting my vanilla Xamarin app to use Prism Library.
The current app uses Azure ADB2C for authorisation using this framework.
Android needs to have its parent window set, which is achieved by adding this code into the MainActivity.cs of the Android project:
var authenticationService = DependencyService.Get<IAuthenticationService>();
authenticationService.SetParent(this);
This doesn't work for the Prism app, authenticationService is null. For the record, the DependencyService used here is Xamarin.Forms.DependencyService.
I also tried the example from the Prism docs and put this code into the AndroidInitializer:
public void RegisterTypes(IContainerRegistry container)
{
// Register any platform specific implementations
container.RegisterSingleton<IAuthenticationService, B2CAuthenticationService>("B2CAuthenticationService");
var authService = Container.Resolve<IAuthenticationService>();
authService.SetParent(this);
}
In this code, Container (which is a DryIoC Container) had no definition for Resolve.
For completeness, this is my App.cs RegisterTypes:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IAuthenticationService, B2CAuthenticationService>();
...
...
}
There are a few wrong assumptions you're making here. To start you'll notice that IContainerRegistry specifically has the name Registry in it to imply we don't want you resolving types here. This is why you don't see the Resolve method on it but rather the IContainerProvider instance.
By design, Prism no longer works directly with the Xamarin.Forms DependencyService as this is a complete anti-pattern. That said if you follow the guidance for registering Platform Specific types you can see how to use the IPlatformInitializer to register platform specific types. It is important to realize here that the IPlatformInitializer is called before RegisterTypes is called in PrismApplication.
What I would suggest is to introduce a IParentWindowProvider like:
public interface IParentWindowProvider
{
object Parent { get; }
}
You can then implement this on Android like:
public class MainActivity : IPlatformInitializer, IParentWindowProvider
{
object IParentWindowProvider.Parent => this;
void IPlatformInitializer.RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IParentWindowProvider>(this);
}
}
Then in your Application you might do something like:
protected override void OnInitialized()
{
if(Container.IsRegistered<IParentWindowProvider>())
{
var provider = Container.Resolve<IParentWindowProvider>();
var authService = Container.Resolve<IAuthenticationService>();
authService.SetParent(provider.Parent);
}
}
For more info be sure to check out the relevant docs and sample
https://prismlibrary.com/docs/xamarin-forms/dependency-injection/platform-specific-services.html
https://github.com/PrismLibrary/Prism-Samples-Forms/tree/master/03-PlatformSpecificServices
I'm trying to create a library such as retro-lambda that reads the code at edit time or compile time and provides hints for the developer.
Let's say I'm using retro-lambda library and I created an interface that has only one method:
public interface Callback{
void onResult(boolean isSuccess, Object result);
}
When I create an instance:
Callback callback = new Callback() {
public void onResult(boolean isSuccess, Object result) {
}
}
Here the retro-lambda will create a hind for the developer to use the lambda function as following:
Callback callback = (isSuccess, result) -> {
}
What do I need to learn about how to create library that inspects the code and adds hints to the user ?
For providing an IDE hint you will want to build an Android Studio plugin.
For inspecting, modifying or generating Java source code at compile-time you will want an annotation processor.
So I want to implement a twitter login into my Xamarin App, but if I follow the tutorial, the Portable App will not let me do this function:
var ui = auth.GetUI(this);
when auth is this:
var auth = new OAuth1Authenticator(
"DynVhdIjJDXXXXXXXXXXXXX",
"REvU5dCUQI4MvjV6aWwXXXXXXXXXXXXXXXXXXXXXXX",
new Uri("https://api.twitter.com/oauth/request_token"),
new Uri("https://api.twitter.com/oauth/authorize"),
new Uri("https://api.twitter.com/oauth/access_token"),
new Uri("http://twitter.com"));
So I need to add it into the Android Project, but how do I show the button from there?
Thanks for any help! :)
Based on OAuth1Authenticator and GetUI() I assume you use Xamarin.Auth.
Xamarin.Auth supports Xamarin.Forms with 2 implementations for GetUI(), so called Presenters (actually Dependency Service/Injection) and CustomRenderers. Presenters are more tested (thus more evidence of stabilty), but you can check the code in the repo.
Samples how to use Xamarin.Auth with the Xamarin.Forms:
https://github.com/moljac/Xamarin.Auth.Samples.NugetReferences/tree/master/Xamarin.Forms/Evolve16Labs
NOTE: this repo contains extracted samples from the main Xamarin.Auth repo, so the handling is easier and samples are updated more often.
Creating platform specific code in Xamarin?
You can use DependencyService to implement this function. Here is the document and example. The document explain that :
DependencyService allows apps to call into platform-specific functionality from shared code. This functionality enables Xamarin.Forms apps to do anything that a native app can do.
Create a ILogin interface in PCL :
public interface ILogin
{
void GetUI();
}
Implement the interface in Xamarin.Android :
[assembly: Xamarin.Forms.Dependency(typeof(TwitterLogin))]
namespace TwitterDemo.Droid
{
public class TwitterLogin : ILogin
{
public TwitterLogin()
{
}
public void GetUI()
{
var auth = new OAuth1Authenticator("DynVhdIjJDXXXXXXXXXXXXX", "REvU5dCUQI4MvjV6aWwXXXXXXXXXXXXXXXXXXXXXXX",
new Uri("https://api.twitter.com/oauth/request_token"),
new Uri("https://api.twitter.com/oauth/authorize"),
new Uri("https://api.twitter.com/oauth/access_token"),
new Uri("http://twitter.com"));
Intent intent = auth.GetUI(Android.App.Application.Context);
Forms.Context.StartActivity(intent);
}
}
}
Use it in Xamarin.Forms:
private void Button_Clicked(object sender, EventArgs e)
{
Xamarin.Forms.DependencyService.Register<ILogin>();
DependencyService.Get<ILogin>().GetUI();
}
I am quite new at Unity and I am trying to create a Unity plugin for an Android library jar and I am facing the following issues:
I can't find a way to pass the back button event to Android library. It seems that Unity prevents this event to be passed to the library layer. What I have managed to do is to pass touch events to the activity on the Android side by setting the following line
<meta-data android:name="unityplayer.ForwardNativeEventsToDal vik" android:value="true" />
at <activity android:name="com.unity3d.player.UnityPlayerProxyActivity" ... > in the AndroidManifest
However I cannot pass the back button event in a way that I do not have to change the code in the library
What I do for now in my script is
public void Update() {
if(Input.GetKeyDown(KeyCode.Escape))
Application.Quit();
}
However I need the option to pass that back event that I capture, to the library and not handle it at the Unity layer.
Maybe somehow like this.
myActivity.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
or this
myActivity.onBackPressed()
but through a C# script. How can I accomplish that within a script?
C# script:
void Update ()
{
if (Input.GetKeyUp (KeyCode.Escape)) {
AndroidJavaClass jc = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject> ("currentActivity");
jo.Call ("yourBackEventFunction");
}
}
And your Android lib
public class YourActivity extends
com.unity3d.player.UnityPlayerNativeActivity {
…
public void yourBackEventFunction() {
// Do something here
}
…
}
========================
Edit:
If you want to call onBackPressed, you can do so:
In YourActivity.java
#Override
public void onBackPressed() {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(UnityPlayerNativeActivity.this,
"on Back Pressed", Toast.LENGTH_SHORT)
.show();
}
});
super.onBackPressed();
}
And in C# script:
void Update ()
{
if (Input.GetKeyUp (KeyCode.Escape)) {
Debug.Log ("onResume Received");
AndroidJavaClass jc = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject> ("currentActivity");
jo.Call ("onBackPressed");
}
}
Or inside YourActivity you can:
Create your YourActivity Extends UnityPlayerNativeActivty
Include your Jar lib and call the function you want.
Use C# script to call your Activity.
Note that in Unity, there is only 1 Activity, and it must be UnityPlayerNativeActivity OR it must be an Activity EXTENDS FROM UnityPlayerNativeActivity. You can not use any others activity from your Jar without extend from UnityPlayerNativeActivity.
If YourActivity class inside JAR extends UnityPlayerNativeActivity. and you don't want to change your JAR, then you create a new Activity class extends YourActivity. Create a new Jar + old Jar and make a new plugins.
========================
If you want to call a function directly from C# to Java class, you still can do it by using Android NDK to build an JNI lib
This sample demonstrates how Java code can be used to interact with
the Android OS and how C++ creates a bridge between C# and Java. The
scene in the package displays a button which when clicked fetches the
application cache directory, as defined by the Android OS. Please note
that you will need both the JDK and the Android NDK to compile the
plugins.
Reference here: http://docs.unity3d.com/Documentation/Manual/PluginsForAndroid.html
Example here: http://docs.unity3d.com/Documentation/Images/manual/AndroidJavaPlugin.zip
But I'm not sure if you can use your own Activity or not. And I recommend creating a new Activity Extends from UnityPlayerNativeActivty, because that way is more simple and unstandable than this way.
Br,
Frank
In an effort to reduce duplication, I have my app's workspace split into 3 projects:
Main (A library project, where all of the common code lives)
Free (To make the free version)
Paid (To make the paid version)
Behavior in the free version and the paid version must differ sometimes. How can I "call into" the final projects from the library project?
Here is some sample psuedo-code to illustrate my question:
In the Main project:
private void makeADecision() {
if (AppCode.isPaid()) {
// do one thing
} else {
// do something else
}
}
In the Free project:
public class AppCode {
public static bool isPaid() {
return false;
}
}
In the Paid project:
public class AppCode {
public static bool isPaid() {
return true;
}
}
That is basically what I have tried, but it won't compile because the Main project doesn't know about the AppCode class.
Bear in mind that this is only an example, so try not to focus on how an app can tell if it is the paid version or not. :) So far the best solution I have found is to put a string in the resources of all three projects and then make a decision based on its value but I don't like that method. Besides being ugly, I would prefer to keep functionality where it belongs. That way I can prevent "paid-only" functionality from being compiled into the free version at all and I can avoid having to include any "free-only" code in the paid version.
Step #1: Define an interface in the library, so it is available to all three parties, whose methods are whatever operations you want the library to perform on the app
Step #2: Have the app supply an implementation of the interface to the library via some library-supplied API
Step #3: Have the library call methods on the supplied interface as needed
I don't think it's a good idea to call the main App from the library, even if it's possible.
Instead I'd be adding a public static boolean to the library and set it from within your application once it starts for the first time.
public class MyLibrary {
public static boolean IS_PAID = false;
public void makeADecision() {
if(IS_PAID) {
// do one thing
} else {
// do something else
}
}
}
and in your main application you could do something like
com.yourname.yourlib.MyLibrary.IS_PAID = true;
to set it. Since it's not final, you can change it's state at any time. If it's more complicated behavior, you could use a public static listener or callback which you could assign from your full/free app and then call it from your library
You could use reflection to achieve that - take care that it is usually not a very good idea.
For example:
static private boolean isAppPaid;
static {
try {
Class c = Class.forName("your.package.AppCode");
Method m = c.getMethod("isPaid");
isAppPaid = (boolean) m.invoke(null);
}
catch (Exception e) {
isAppPaid = false;
}
}
There probably are mistakes in my code - I have never used Java reflection much.
Edit: I agree with Tseng that making a library invoke application code is debatable at best. (Except if said library is a framework that takes over the client application.)
You could also make the free and paid versions make subclasses of whatever class makeADecision is in and implement the separate behavior that way.
so in main
public class BaseClass {
...
public void makeADecision() {}
...
}
in free
public class FreeClass extends BaseClass {
...
public void makeADecision() {
//free implementation here
}
...
}
in paid
public class PaidClass extends BaseClass {
...
public void makeADecision() {
//paid implementation here
}
...
}
Tseng Solution is an easy and straightforward solution. Thanks for that, it is the one i have used. Also, I fell upon that article which could help you implement the solution :
http://www.firstlightassociates.co.uk/blog/2011/software/android-software/managing-free-and-paid-android-applications-2/
Hope it will help some of you !
Cheers