Proguard stops Javascript in WebView from working - android-webview

I have a class JSBridge (an inner class) which is a javascript interface:
private class JsBridge implements JsCallback {
/**
* #param handlerName method required
* #param jsonData data passed through from javascript
* #param jsCallback A callback to trigger when handler specified by handlername has finished, could be null
*/
#JavascriptInterface
public void callHandler(final String handlerName, final String jsonData, final String jsCallback) {
Log.d(App.TAG, "Bridge call from JS, received " + handlerName);
}
#JavascriptInterface
public void onPageLoad(final String pageName) {
Log.d(App.TAG, "Bridge call from JS, received onPageLoad - we have the page name " + pageName);
}
This works fine until I do a release build with proguard. I've tried following some other SO answers and have added the following lines to my proguard file, but it has not helped. The result is the debug version I get the callbacks, the release version I get no callbacks.
-keep public class * implements com.mixcloud.player.view.JsCallback
-keepclassmembers class * implements com.mixcloud.player.view.JsCallback {
<methods>;
}
-keep public class * implements com.mixcloud.player.view.JsCallback
-keepattributes *Annotation*
-keepattributes JavascriptInterface
-keep public class com.mixcloud.player.view.JSRefreshWebView
-keep public class com.mixcloud.player.view.JSRefreshWebView$JsBridge
-keep public class * implements com.mixcloud.player.view.JSRefreshWebView$JsBridge
-keepclassmembers class * implements com.mixcloud.player.view.JSRefreshWebView$JsBridge {
<methods>;
}

If your Javascript interface methods are annotated with #JavascriptInterface, you can preserve them with
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}

Related

How to pass multiple parameters in UiChild tags in GWT?

The GWT xml looks like:
<my:CustomWidget>
<my:tag para1="a" para2="b"/>
</my:CustomWidget>
And how can I get these 2 parameters in Java code? Similar to the codes below:
#UiChild(tagname = "item")
public void addItem(String para1, String para2) {
//......
}
PS: the above Java code doesn't work.
Is there any other ways to get parameters by using UiChild tag in GWT?
You should have the #UiChild at your CustomWidget level:
class MyCustomWidget extends Composite {
#UiChild(tagname = "tag")
public void addTag(Tag tag) {
/* ... */
}
}
and the parameters in your constructor's Tag class, annotated with #UiConstructor:
class Tag extends Composite {
#UiConstructor
public Tag(String para1, String para2) {
/* ... */
}
}

Social Authentication crashes after obfuscating with proguard

I have an application which uses social authentication (Twitter and Facebook). Before publishing it in app store I used proguard to obfuscate my code. All features work fine except authentication of Facebook and Twitter. As soon as I click on Login with facebook or login with twitter button the application crashes with message Unfortunately "APP Name" has stopped.
My twitter authentication class extends Activity and TwiterProfile.java class extends fragments.
here is my proguard configuration
-injars bin/classes
-injars libs
-outjars bin/classes-processed.jar
-libraryjars libs\ksoap2-android-assembly-2.4-jar-with-dependencies.jar
-libraryjars libs\twitter4j-core-4.0.1.jar
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
-dontwarn twitter4j.**
-dontwarn org.xmlpull.v1.**
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.support.v4.app.ListFragment
-keep public class * extends android.app.Fragment
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
public void *(android.view.MenuItem);
}
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}
Here is twitterauthentication.java which is called from TwitterProfile.java class
public class TwitterAuthenticate extends Activity{
private WebView webView;
public static String EXTRA_URL = "extra_url";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
setTitle("Twitter");
final String url = this.getIntent().getStringExtra(EXTRA_URL);
if (null == url) {
Log.e("Twitter", "URL cannot be null");
finish();
}
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new MyWebViewClient());
webView.loadUrl(url);
}
class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains(getResources().getString(R.string.twitter_callback))) {
Uri uri = Uri.parse(url);
/* Sending results back */
String verifier = uri.getQueryParameter(getString(R.string.twitter_oauth_verifier));
Intent resultIntent = new Intent();
resultIntent.putExtra(getString(R.string.twitter_oauth_verifier), verifier);
setResult(RESULT_OK, resultIntent);
/* closing webview */
finish();
return true;
}
return false;
}
}
}

Extend function with Doctrine ORM Annotation

I've got the following situation: a (Doctrine Entity) ContentCategory that is extending the DataObject class. The DataObject class has the following function, onPrePersist:
/**
* #ORM\HasLifecycleCallbacks
*/
class DataObject implements InputFilterAwareInterface
{
...
/** #ORM\PrePersist */
public function onPrePersist()
{
//using Doctrine DateTime here
$this->creation_date = new \DateTime('now');
}
The ContentCategory class needs this function aswell. When I put this function in the ContentCategory class it works just fine. Is there a way whereby, the ContentCategory class can use the same function, onPrePersist() without defining it in the class itsself?
* #ORM\HasLifecycleCallbacks()
*/
class ContentCategory extends DataObject implements InputFilterAwareInterface
{
...
}
The reason to give objects the onPrePersist function, is to set a DateTime when this object is created or any other object / entity that is extending the DataObject class.
--< Edited >--
I've currently added a construct method to the ContentCategory like this:
public function __construct() {
parent::onPrePersist();
}
In this way Doctrine executes the function onPersist when a new Entity is created. The other case is when an enttiy is being updated, with Doctrine. I'll like to set a Modified_date. In that case there will be a function like this, in the DataObject class.
/**
* #ORM\HasLifecycleCallbacks
*/
class DataObject implements InputFilterAwareInterface
{
...
/**
* #ORM\PreUpdate
*/
public function onUpdate()
{
$this->last_modified_date = new \DateTime('now');
}
The Doctrine ORM Annotation (PreUpdate) that have been added, will make sure that the function (above) will be excuted on an update statement for an object. The problem is, how to call those functions in an object which extends the DataObject
/**
* #ORM\MappedSuperclass
* #ORM\HasLifecycleCallbacks
*/
class TestimonialSuperclass
{
/**
* #ORM\PreFlush
*/
public function onPreFlush ()
{
echo 123;
}
}
/**
* #ORM\Entity
* #ORM\Table(name="testimonials")
* #ORM\HasLifecycleCallbacks
*/
class Testimonial extends TestimonialSuperclass
{
...
}

AndroidAnnotations: #Rest error converting Json after export

I have these classes:
public class OfferList {
public List<Offer> offers;
}
public class Offer {
public String offer_id;
public String group_id;
public String n_hours;
public boolean is_new;
}
And defined this service:
#Rest(rootUrl = "http://MYURL/json", converters = { GsonHttpMessageConverter.class })
//if defined, the url will be added as a prefix to every request
public interface EscolasPertoDeMimRESTService {
#Get("/offers/{user_id}/{date}")
#Accept(MediaType.APPLICATION_JSON)
OfferList getOffers(String user_id, long date);
}
Which is called here:
(...)
#RestService
OfferRestService offersService;
(...)
#Background
void loadListItems() {
mLoadingOffers = true;
showProgressDialog();
OfferList ol = null;
try {
ol = offersService.getOffers(myPrefs.userID().get(), myPrefs.lastCheckedForOffers().get());
showDebug("ol.offers.size(): " + ol.offers.size()); //OK shows ol.offers.size(): 24
Offer o = ol.offers.get(0); //After exporting: Crash
showDebug("ol.offers[0].group_id" + o.group_id);
} catch (Exception e) {
showDebug(e.getMessage()); //After exporting shows: "com.google.gson.internal.StringMap cannot be cast to com.humihara.escolaspertodemim.Offer"
}
setupAdapter(ol);
mLoadingOffers = false;
}
(...)
Everything works fine in debug, but when I export and sign, the app crashes.
The GET is sent and the response from the server is a valid JSON offer list.
Apparently the result I get is an OfferList with StringMap(s) instead of Offer(s).
This is my proguard-project.txt (I'm using the android sdk defaults plus these):
-keep public class com.humihara.escolaspertodemim.** { *; }
-keep public class org.springframework.** { *; }
-keep public class com.google.gson.** { *; }
-keep public class com.google.ads.** { *; }
-keep public class com.androidannotations.** { *; }
-keep public class org.acra.** { *; }
-keep public class * extends android.support.v4.app.FragmentActivity
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet); }
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int); }
-dontwarn org.simpleframework.**
-dontnote org.simpleframework.**
-dontwarn org.codehaus.jackson.**
-dontnote org.codehaus.jackson.**
-dontwarn com.fasterxml.jackson.**
-dontnote com.fasterxml.jackson.**
-dontwarn com.google.code.rome.**
-dontnote com.google.code.rome.**
-dontwarn org.apache.commons.httpclient.**
-dontnote org.apache.commons.httpclient.**
I have searched the mailing list plus stackoverflow where I picked up the extra definitions for proguard, but now I don't know what else to do.
Can someone please tell me what I must change in my code or proguard-project.txt to make it work?
UPDATE:
After checking Jackson Json parser returns nulls on all object members on Android after optimizing with Proguard
I guess I was missing:
-dontskipnonpubliclibraryclassmembers
The default android proguard defines:
-dontskipnonpubliclibraryclasses
and I didn't noticed they aren't the same expression.
I ended up with this proguard-project.txt:
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
-dontskipnonpubliclibraryclassmembers
-keep class com.humihara.escolaspertodemim.** { *; }
-keep public class * extends android.support.v4.app.FragmentActivity
-dontwarn org.simpleframework.**
-dontnote org.simpleframework.**
-dontwarn org.codehaus.jackson.**
-dontnote org.codehaus.jackson.**
-dontwarn com.fasterxml.jackson.**
-dontnote com.fasterxml.jackson.**
-dontwarn com.google.code.rome.**
-dontnote com.google.code.rome.**
-dontwarn org.apache.commons.httpclient.**
-dontnote org.apache.commons.httpclient.**
And now everything works fine.
For code that uses reflection to access annotations, you should preserve the annotations:
-keepattributes *Annotation*

Intercepting Async Proxy Service exceptions for GWT RPC

My app has many RPC calls, and they all have a .onFailure(Throwable caught) method. I have a class shared between the client and the server code NotLoggedInException. This is thrown by the server, if the user doesn't have the relevant permissions based on sessions/cookies/permissions etc.
Ideally I would like to handle this exception in one place BEFORE others are passed to the .onFailure() code, given how ubiquitous this handling is and needs to be for security. There is a GWT.setUncaughtExceptionHandler() but this appears to get called after the handling which isn't ideal (in case an .onFailure accidentally consumes too much).
Does anybody have an elegant solution to this? An ugly solution is to wrap the deferred binded .create() proxy in the same aggregated class implementing the async interface.
Sidenote: The server was issuing a redirect before, but I don't like this paradigm, and would prefer it to be handled by the eventbus of the app.
Update: ugly answer referred to above
public abstract class CustomAsyncCallback implements AsyncCallback{
#Override
public CustomAsyncCallback(AsyncCallback<T> callback)
{
this.wrap = callback ;
}
AsyncCallback<T> wrap ;
#Override
public void onFailure(Throwable caught) {
if (!handleException())
{
wrap.onFailure(caught) ;
}
}
#Override
public void onSuccess(T t) {
wrap.onSuccess(t) ;
}
}
public class WrapDeferredBinding implements RpcInterfaceAsync
{
RpcInterfaceAsync service = GWT.create(RpcInterface.class);
public void method1(int arg1, AsyncCallback<Boolean> callback)
{
service.method1(arg1, new CustomAsyncCallback<Boolean>(callback)) ;
}
public void method2 ....
public void method3 ....
}
In order to wrap every AsynCallback<T> that is passed to any RemoteService you need to override RemoteServiceProxy#doCreateRequestCallback() because every AsynCallback<T> is handed in here before an RPC call happens.
Here are the steps to do so:
To begin you need to define your own Proxy Generator to step in every time a RemoteService proxy gets generated. Start by extending ServiceInterfaceProxyGenerator and overriding #createProxyCreator().
/**
* This Generator extends the default GWT {#link ServiceInterfaceProxyGenerator} and replaces it in the
* co.company.MyModule GWT module for all types that are assignable to
* {#link com.google.gwt.user.client.rpc.RemoteService}. Instead of the default GWT {#link ProxyCreator} it provides the
* {#link MyProxyCreator}.
*/
public class MyServiceInterfaceProxyGenerator extends ServiceInterfaceProxyGenerator {
#Override
protected ProxyCreator createProxyCreator(JClassType remoteService) {
return new MyProxyCreator(remoteService);
}
}
In your MyModule.gwt.xml make use of deferred binding to instruct GWT to compile using your Proxy Generator whenever it generates something of the type RemoteService:
<generate-with
class="com.company.ourapp.rebind.rpc.MyServiceInterfaceProxyGenerator">
<when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService"/>
</generate-with>
Extend ProxyCreator and override #getProxySupertype(). Use it in MyServiceInterfaceProxyGenerator#createProxyCreator() so that you can define the base class for all the generated RemoteServiceProxies.
/**
* This proxy creator extends the default GWT {#link ProxyCreator} and replaces {#link RemoteServiceProxy} as base class
* of proxies with {#link MyRemoteServiceProxy}.
*/
public class MyProxyCreator extends ProxyCreator {
public MyProxyCreator(JClassType serviceIntf) {
super(serviceIntf);
}
#Override
protected Class<? extends RemoteServiceProxy> getProxySupertype() {
return MyRemoteServiceProxy.class;
}
}
Make sure both your MyProxyCreator and your MyServiceInterfaceProxyGenerator are located in a package that will not get cross-compiled by GWT into javascript. Otherwise you will see an error like this:
[ERROR] Line XX: No source code is available for type com.google.gwt.user.rebind.rpc.ProxyCreator; did you forget to inherit a required module?
You are now ready to extend RemoteServiceProxy and override #doCreateRequestCallback()! Here you can do anything you like and apply it to every callback that goes to your server. Make sure that you add this class, and any other class you use here, in my case AsyncCallbackProxy, to your client package to be cross-compiled.
/**
* The remote service proxy extends default GWT {#link RemoteServiceProxy} and proxies the {#link AsyncCallback} with
* the {#link AsyncCallbackProxy}.
*/
public class MyRemoteServiceProxy extends RemoteServiceProxy {
public MyRemoteServiceProxy(String moduleBaseURL, String remoteServiceRelativePath, String serializationPolicyName,
Serializer serializer) {
super(moduleBaseURL, remoteServiceRelativePath, serializationPolicyName, serializer);
}
#Override
protected <T> RequestCallback doCreateRequestCallback(RequestCallbackAdapter.ResponseReader responseReader,
String methodName, RpcStatsContext statsContext,
AsyncCallback<T> callback) {
return super.doCreateRequestCallback(responseReader, methodName, statsContext, new AsyncCallbackProxy<T>(callback));
}
}
Now, your AsyncCallbackProxy can look something like this:
public class AsyncCallbackProxy<T> implements AsyncCallback<T> {
private AsyncCallback<T> delegate;
public AsyncCallbackProxy(AsyncCallback<T> delegate) {
this.delegate = delegate;
}
#Override
public final void onFailure(Throwable caught) {
GWT.log("AsyncCallbackProxy#onFailure() : " + caught.getMessage(), caught);
if (caught instanceof NotLoggedInException) {
// Handle it here
}
delegate.onFailure(proxy);
}
#Override
public final void onSuccess(T result) {
delegate.onSuccess(result);
}
}
References:
DevGuideCodingBasicsDeferred.html
An example applied to performance tracking
You can wrap AsyncCallback class with an abstract class:
public abstract class CustomAsyncCallback<T> implements AsyncCallback<T>{
#Override
public void onFailure(Throwable caught) {
GWT.log(caught.getMessage());
handleException();
this.customOnFailure(yourDesireParam);
}
/**
* this method is optional
*/
public abstract void customOnFailure(Param yourDesireParam);
}
And then send a CustomAsyncCallback object to your RPC asynch methods.