Android - ProGuard causing warnings from all JavascriptInterface uses - javascript

I have seen lots of problems with using ProGuard on applications that include webviews with JavascriptInterfaces, but none of the solutions seem to work for me so I must be missing something.
I have this activity
public class MapviewActivity extends Activity {
private WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview_map);
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle("The Map");
webView = (WebView) findViewById(R.id.map_webview);
webView.clearCache(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setSupportZoom(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setUseWideViewPort(true);
webView.setInitialScale(1);
webView.getSettings().setUseWideViewPort(true);
webView.loadUrl("file:///android_asset/map.html");
webView.addJavascriptInterface(new MapviewJSInterface(this), "Android");
}
public class MapviewJSInterface {
public Context mContext;
public MapviewJSInterface(MapviewActivity mapviewActivity) {
this.mContext = mapviewActivity;
}
#JavascriptInterface
public void showResidents(String locCode) {
Intent intent = new Intent(mContext, DialogResidentsList.class);
intent.putExtra("colorString", locCode);
mContext.startActivity(intent);
}
}
}
my proguard-project.txt includes this
-keep class tv.cmc2.zaratancodex.controller.MapviewActivity$MapviewJSInterface
-keepclassmembers class tv.cmc2.zaratancodex.controller.MapviewActivity$MapviewJSInterface {
public void showResidents(java.lang.String);
}
but I keep getting this error when I try to export an apk
Warning: tv.cmc2.zaratancodex.view.MapviewActivity$MapviewJSInterface: can't find referenced class android.webkit.JavascriptInterface
there must be something else that I am missing because those are the rules that seem to be working for everyone else.

You should build against a version of the Android runtime that contains the class android.webkit.JavascriptInterface. That would be android-17 or higher. ProGuard then won't complain that it can't find it.
If your code uses the annotation to mark Javascript interface classes (as required in recent versions of the Android SDK), you can also use it in your ProGuard configuration:
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}
You then no longer need to exhaustively list the interface classes and methods.

Related

React Native Android Webview Video

I'm using React Native to create an Android/iOS app and trying to get a video to play in the WebView component. The video plays fine on iOS, but I'm having trouble getting it to play in the android WebView.
I've come across a few threads like this one (Enabling HTML5 video playback in android WebView?) that claim this is a fairly common problem on Android and can be solved by importing WebChromeClient and setting that option on the webview like so:
mainWebView.setWebChromeClient(new WebChromeClient());
But almost all these threads are strictly about building a native Android app and not using React Native.
Does anyone know how to get this to work in React Native?
I refer to an article by Yevgen Safronov
In it, he writes
Obviously the most challenging part of the application is handling
live video stream, because it requires switching stream’s video
quality based on available Internet bandwidth. But first things
first — I needed a RN native component to show any video stream. There
is a popular video component for RN but it has support for iOS only. I
decided to write my own RN component wrapper around Vitamio player. It
is well known open-source project and has support of RTMP protocol we
use for mobile app.
I had no prior experience with writing native RN components so I went
directly to RN documentation on how to create one. A guide I refer to
is called Native UI Components, there is similar one for iOS. There
are several essential parts to declare:
Implement custom ViewManager (Android part)
Register the ViewManager (Android part)
Implement the JavaScript module
Register the module (Android part)
Implement custom ViewManager Referring to the example of declaring
VideoView for Vitamio this is how the essence of VideoView declaration
looks like:
public class VideoViewDemo extends Activity {
#Override public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (!LibsChecker.checkVitamioLibs(this))
return;
setContentView(R.layout.videoview);
mEditText = (EditText) findViewById(R.id.url);
mVideoView = (VideoView) findViewById(R.id.surface_view);
if (path == "") { return; }
mVideoView.setVideoPath(path);
mVideoView.setMediaController(new MediaController(this));
mVideoView.requestFocus();
}
...
}
The code looks quite straightforward. Apart from passing a reference
to Activity into LibsChecker, VideoView requires a path to a video
stream and instance of MediaController.
public class VitamioViewManager extends SimpleViewManager<VideoView>{
public static final String REACT_CLASS = “RCTVitamioView”;
#Override
public String getName() {
return REACT_CLASS;
}
expose setStreamUrl setter using ReactProp:
#ReactProp(name = "streamUrl")
public void setStreamUrl(VideoView view, #Nullable String streamUrl) {
if (!LibsChecker.checkVitamioLibs(mActivity))
return;
view.setVideoPath(streamUrl);
view.setMediaController(new MediaController(mContext));
view.requestFocus();
}
add createViewInstance implementation:
private ThemedReactContext mContext = null;
private Activity mActivity = null;
#Override
public VideoView createViewInstance(ThemedReactContext context){
mContext = context;
return new VideoView(context);
}
One note about the code. Because LibsChecker requires an instance of Activity we will receive it via constructor, it will reference root activity used for RN application;
public VitamioViewManager(Activity activity) {
mActivity = activity;
}
Register the ViewManager
The final Java step is to register the ViewManager to the application, this happens via the applications package member function createViewManagers:
...
public class VitamioViewPackage implements ReactPackage {
private Activity mActivity = null;
public VitamioViewPackage(Activity activity) {
mActivity = activity;
}
#Override
public List<NativeModule>
createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
#Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
#Override
public List<ViewManager>
createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new VitamioViewManager(mActivity)
);
}
}
Implement the JavaScript module In order to expose custom UI component
in JavaScript it is necessary to call special requireNativeComponent
function:
var { requireNativeComponent, PropTypes } = require('react-native');
var iface = {
name: 'VideoView',
propTypes: {
streamUrl: PropTypes.string
}
};
module.exports = requireNativeComponent('RCTVitamioView', iface);
Register the module Although it’s not mentioned as required step in
official documentation we need it because of reference to the root
activity: package com.vitamio_demo;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import java.util.Arrays;
import java.util.List;
import com.sejoker.VitamView.VitamioViewPackage; // <--- import
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
#Override
protected String getMainComponentName() {
return "vitamio_demo";
}
/**
* Returns whether dev mode should be enabled.
* This enables e.g. the dev menu.
*/
#Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
#Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VitamioViewPackage(this) // <------ add here
);
}
}
Example of usage
Install the package in a project:
npm i react-native-android-vitamio --save
DeclareVideoView:
var VitamioView = require('react-native-android-vitamio');
class VideoScreen extends React.Component {
render() {
return (
<View>
<VitamioView style={styles.video} streamUrl="rtmp://fms.12E5.edgecastcdn.net/0012E5/mp4:videos/8Juv1MVa-485.mp4"/>
</View>
);
}
}
var styles = StyleSheet.create({
video: {
flex: 1,
flexDirection: 'row',
height: 400,
}
})
module.exports = VideoScreen;
Hope this is of help, A list of his own references is given in the article.

How can I calling JavaScript in WebView from Android?

I am beginner in Android development and want to create Android app with JavaScript.
I can run my page in WebView.
I can run JavaScript in WebView.
I can call Android Toast message (or Dailog alert ) from JavaScript to Android.
But I can NOT run JavaScript function from Android !
Can you help me to fix my code
[ Download an android studio project ]
http://r7eq.weebly.com
Code of MainActivity.java
package com.example.adnroid_script;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
public class MainActivity extends Activity {
public static WebView myWebView = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myWebView=(WebView) findViewById(R.id.webview);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.addJavascriptInterface(new jsInterface(), "FromJS");
myWebView.loadUrl("file:///android_asset/index.html");
}
public class jsInterface{
#JavascriptInterface
public void ToAndroid(String jsMessage){
Toast toast = Toast.makeText(getApplicationContext(),"I sent javascript code to myWebView", Toast.LENGTH_LONG);
toast.show();
WebView myWebView = (WebView) findViewById(R.id.webview);
// ### The bug is here ### //
myWebView.loadUrl("javascript:document.write('<div>Android say : It is working now !</div>');");
}
}
}

Android Javascript Interface empty object in webview

I am trying to set up a simple Android app that displays a webview and has some extra functionality available via the Javascript Interface.
I am testing on a HTC One S running Kitkat (via Cyanogenmod). When I inspect the webview the object inserted (Android) is empty "Object {}". I cannot use the functions in the interface via the loaded page.
I had built a more complicated activity but could not get the interface working, so I've created this new app for testing which contains the only the code from the webview guides. Still not inserting the methods into the exposed object.
MyActivity
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.addJavascriptInterface(new WebAppInterface(this), "Android");
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.loadUrl("http://47e148ba.ngrok.com");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.my, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
WebAppInterface
import android.content.Context;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
/**
* Created by graeme on 28/08/2014.
*/
public class WebAppInterface {
Context mContext;
/** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
Layout
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "com.mydomain.jswebview"
minSdkVersion 17
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
You can see I'm using the decorator required for the methods, which most questions seem to have as the solution. I'm developing in Android Studio if any of the presets in there might be interfering?
Edit:
In The JS file, or via the console if I'm inspecting the webview via chrome if I call Android or window.Android i get an empty objected returned "Object {}". Or if I try to use the methods defined in the interface above I'll receive a method undefined error.
I was having the same problem. Turned out to be a Proguard problem. Try adding the following to your Proguard rules:
-keepclassmembers class ** {
#android.webkit.JavascriptInterface <methods>;
}
I don't know how are you trying to call to your Java methods from inside of your WebView. I think that the correct way is:
javascript:window.Android.showToast('Hi!');
you could also test your JS interface from your Java code using:
myWebView.loadUrl("javascript:window.Android.showToast('Hi!');");
I hope this help you!

Is it possible to use JavaScript code in Android?

in this code Android app opens a web page with WebView and extracts a text from HTML which is between tags "body" and "/body".
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.TextView;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
public class MainAc extends Activity {
/** Called when the activity is first created. */
#SuppressLint({ "JavascriptInterface", "SetJavaScriptEnabled" })
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView webView = (WebView) findViewById(R.id.web);
TextView text2 = (TextView) findViewById(R.id.text);
Button infoButton = (Button) findViewById(R.id.b1);
infoButton.setOnClickListener(new OnClickListener(){
public void onClick(View view){
// here is your button click logic, for example running another activity (page)
startActivity(new Intent(MainAc.this, JavaInterface.class));
}
});
class Javasc {
private TextView t2;
public Javasc (TextView i)
{
t2 = i;
}
#SuppressWarnings("unused")
public void processContent(String ii)
{
final String content = ii;
t2.post(new Runnable()
{
public void run()
{
t2.setText(content);
}
});
}
}
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new Javasc(text2), "INTERFACE");
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
view.loadUrl("javascript:window.INTERFACE.processContent(document.getElementsByTagName('body')[0].innerText);");
}
});
webView.loadUrl("http://www.nytimes.com/2014/08/03/sports/basketball/pacers-paul-george-has-surgery-after-badly-injuring-leg.html?ref=sports");
}
}
Is it possible to use JavaScript functions for extracted text in android's TextView ?
for example this JavaScript function (or it could be any other JS function where need to work with text)
function myFunction() {
var text = document.body.innerText;
var titles =text.match(/^\n(.+?)\n\n/mg);
for (var i = 0; i < titles.length; i++) {
document.write(titles[i] + "<br />" + "<br />");
}
}
Thanks for answers :)
According to this article, the Dalvik VM supports Java's scripting features (javax.script). One of the premier languages supported by the javax.script stuff is, unsurprisingly, JavaScript.
So in theory, you can use the javax.script stuff to execute JavaScript code and get back results. I think (also based on that article), that you have to include the relevant jar(s) (javax.script isn't in the Android SDK). Fortunately, though, javax.script is largely a set of interfaces, which are implemented by jars for specific scripting languages.
Some resources about using javax.script to run script code:
The Java Scripting API (Oracle)
Java Scripting Programmer's Guide (Oracle)
You can use Rhino to achieve this. See specifically Context.jsToJava.
https://vec.io/posts/embed-javascript-in-android-java-code-with-rhino
You want to do a regex on a text and resorting to javascript for it, which is unnecessary when you have a better faster API in java with no overhead.
The equivalent to your example is:
http://developer.android.com/reference/java/util/regex/Pattern.html
Pattern p = Pattern.compile("/^\n(.+?)\n\n/mg");
Matcher m = p.matcher(myTextView.getText());
while (m.find()) {
String titles = m.group(1);
Log.V("TAG", titles);
}
There are plenty of other text analysis solutions included in the core framework or external libraries that have been proved for both mobile and server. You just have to look it up and not resort to a worse API you're comfortable with.

Proguard mess Javascript Interface functions when targeting SDK in Android Manifest above 17

I have a custom Webview in my android project as shown below:
public class MyWebView extends WebView {
public MyWebView(Context context) {
super(context);
}
public class JsObject {
#JavascriptInterface
public void show() {
//...
}
#JavascriptInterface
public void hide() {
//....
}
}
that includes a JavascriptInterface which I use to communicate from the JavaScript side to the Android side.
In the AndroidManifest I had the following
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
In the project I used proguard which declared:
-keepattributes JavascriptInterface
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}
and everything was working fine.
However when i changed my AndroidManifest to android:targetSdkVersion=18 or 19 and test on devices with 18 and above, proguard seems to somehow mess the JavaScript methods which are not accessible anymore.
If I put back to 16 or anything less than 17 everything works fine. Additionally this happens only with proguard. If I don't use proguard everything works fine even with android:targetSdkVersion=18 or 19. Can anyone help to make it work when targeting in the manifest Android > 17?
I copy my answer from this topic for you: https://stackoverflow.com/a/19994873/1735499
And, if you are using Proguard, remember to add this
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}
-keepattributes JavascriptInterface
-keep public class com.mypackage.MyClass$MyJavaScriptInterface
-keep public class * implements com.mypackage.MyClass$MyJavaScriptInterface
-keepclassmembers class com.mypackage.MyClass$MyJavaScriptInterface {
<methods>;
}
If it's still not OK, add this
-keepattributes *Annotation*
Note: your MyJavaScriptInterface must be Public class
Ref#: Android Proguard Javascript Interface Fail
Br,
Frank
These 4 lines are usually enough - and no need to make the interface public.
-keepattributes JavascriptInterface
-keepclassmembers class * {
#android.webkit.JavascriptInterface <methods>;
}
In my case, just :
-keepclassmembers class com.mypackage.MyJavaScriptInterface {
public *;
}
-keepattributes *Annotation*
was enough !

Categories

Resources