Android not displaying camera in WebView.
I have build a WebView application with camera access.
The url loads and it works. but camera does not work in WebView application.
My code is below I have my question is what am I doing wrong. I see no errors but the camera does not open at all .
<!DOCTYPE html>
<html>
<head>
<title>///////</title>
<script type="text/javascript" src="../includes/instascan.min.js"></script>
</head>
<body>
<video id="preview" <?php /* style="position:fixed;right:0;bottom:0;min-width:100%;min-height:100%;" */ ?>></video>
<script type="text/javascript">
let opts = {
// Whether to scan continuously for QR codes. If false, use scanner.scan() to manually scan.
// If true, the scanner emits the "scan" event when a QR code is scanned. Default true.
continuous: true,
// The HTML element to use for the camera's video preview. Must be a <video> element.
// When the camera is active, this element will have the "active" CSS class, otherwise,
// it will have the "inactive" class. By default, an invisible element will be created to
// host the video.
video: document.getElementById('preview'),
// Whether to horizontally mirror the video preview. This is helpful when trying to
// scan a QR code with a user-facing camera. Default true.
mirror: false,
// Whether to include the scanned image data as part of the scan result. See the "scan" event
// for image format details. Default false.
captureImage: false,
// Only applies to continuous mode. Whether to actively scan when the tab is not active.
// When false, this reduces CPU usage when the tab is not active. Default true.
backgroundScan: true,
// Only applies to continuous mode. The period, in milliseconds, before the same QR code
// will be recognized in succession. Default 5000 (5 seconds).
refractoryPeriod: 5000,
// Only applies to continuous mode. The period, in rendered frames, between scans. A lower scan period
// increases CPU usage but makes scan response faster. Default 1 (i.e. analyze every frame).
scanPeriod: 1
};
let scanner = new Instascan.Scanner(opts);
scanner.addListener('scan', function (content) {
window.location = "result.php?result="+content;
});
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
if(cameras.length > 1){
scanner.start(cameras[1]);
}
else{
scanner.start(cameras[0]);
}
} else {
console.error('No cameras found.');
}
}).catch(function (e) {
console.error(e);
});
</script>
</body>
</html>
Here is my Android coding :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0099cc"
tools:context=".FullscreenActivity">
<!-- The primary full-screen view. This can be replaced with whatever view
is needed to present your content, e.g. VideoView, SurfaceView,
TextureView, etc. -->
<TextView
android:id="#+id/fullscreen_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:keepScreenOn="true"
android:text="#string/dummy_content"
android:textColor="#33b5e5"
android:textSize="50sp"
android:textStyle="bold" />
<!-- This FrameLayout insets its children based on system windows using
android:fitsSystemWindows. -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<WebView
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<LinearLayout
android:id="#+id/fullscreen_content_controls"
style="?metaButtonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:background="#color/black_overlay"
android:orientation="horizontal"
tools:ignore="UselessParent">
</LinearLayout>
</FrameLayout>
</FrameLayout>
package //////////?????????;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*/
public class FullscreenActivity extends AppCompatActivity {
/**
* Whether or not the system UI should be auto-hidden after
* {#link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
*/
private static final boolean AUTO_HIDE = true;
/**
* If {#link #AUTO_HIDE} is set, the number of milliseconds to wait after
* user interaction before hiding the system UI.
*/
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
/**
* Some older devices needs a small delay between UI widget updates
* and a change of the status and navigation bar.
*/
private static final int UI_ANIMATION_DELAY = 300;
private final Handler mHideHandler = new Handler();
private View mContentView;
private final Runnable mHidePart2Runnable = new Runnable() {
#SuppressLint("InlinedApi")
#Override
public void run() {
// Delayed removal of status and navigation bar
// Note that some of these constants are new as of API 16 (Jelly Bean)
// and API 19 (KitKat). It is safe to use them, as they are inlined
// at compile-time and do nothing on earlier devices.
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
};
private View mControlsView;
private final Runnable mShowPart2Runnable = new Runnable() {
#Override
public void run() {
// Delayed display of UI elements
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.show();
}
mControlsView.setVisibility(View.VISIBLE);
}
};
private boolean mVisible;
private final Runnable mHideRunnable = new Runnable() {
#Override
public void run() {
hide();
}
};
/**
* Touch listener to use for in-layout UI controls to delay hiding the
* system UI. This is to prevent the jarring behavior of controls going away
* while interacting with activity UI.
*/
private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (AUTO_HIDE) {
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
return false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen);
mVisible = true;
mControlsView = findViewById(R.id.fullscreen_content_controls);
mContentView = findViewById(R.id.fullscreen_content);
// Set up the user interaction to manually show or hide the system UI.
mContentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
toggle();
}
});
// Upon interacting with UI controls, delay any scheduled hide()
// operations to prevent the jarring behavior of controls going away
// while interacting with the UI.
final WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebChromeClient(new WebChromeClient());
myWebView.setWebViewClient(new WebViewClient() {
public void onPageStarted(WebView view, String url, Bitmap favicon) {
myWebView.setVisibility(View.GONE);
}
#Override
public void onPageFinished(WebView view, String url) {
myWebView.setVisibility(View.VISIBLE);
}
public void onReceivedError(WebView webview, int i, String s, String s1) {
webview.loadUrl("");
}
});
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.getSettings().setPluginState(WebSettings.PluginState.ON);
myWebView.loadUrl("https://www.twsb.co.za/mobile_app/rep/scan.php");
// myWebView.setWebChromeClient(new WebChromeClient(){
// #TargetApi(Build.VERSION_CODES.LOLLIPOP)
// #Override
// public void onPermissionRequest(final PermissionRequest request) {
// request.grant(request.getResources());
// }
// });
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
private void toggle() {
if (mVisible) {
hide();
} else {
show();
}
}
private void hide() {
// Hide UI first
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
mControlsView.setVisibility(View.GONE);
mVisible = false;
// Schedule a runnable to remove the status and navigation bar after a delay
mHideHandler.removeCallbacks(mShowPart2Runnable);
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
}
#SuppressLint("InlinedApi")
private void show() {
// Show the system bar
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mVisible = true;
// Schedule a runnable to display UI elements after a delay
mHideHandler.removeCallbacks(mHidePart2Runnable);
mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
}
/**
* Schedules a call to hide() in delay milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="??????????????????">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.bluetooth" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".FullscreenActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/app_name"
android:theme="#style/FullscreenTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
if I load the url separately in my internet browser it works and I can
access the camera. how every not thru the app.
There are two way to implement this feature.
Use native call from webview by javascript method
For more information :
Binding JavaScript code to Android code
Set WebChromeClient in Webview :
For Example:
webView.setWebChromeClient(new WebChromeClient() {
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType){
// Update message
mUploadMessage = uploadMsg;
try{
// Create AndroidExampleFolder at sdcard
File imageStorageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)
, "AndroidExampleFolder");
if (!imageStorageDir.exists()) {
// Create AndroidExampleFolder at sdcard
imageStorageDir.mkdirs();
}
// Create camera captured image file path and name
File file = new File(
imageStorageDir + File.separator + "IMG_"
+ String.valueOf(System.currentTimeMillis())
+ ".jpg");
mCapturedImageURI = Uri.fromFile(file);
// Camera capture image intent
final Intent captureIntent = new Intent(
android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
// Create file chooser intent
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
// Set camera intent to file chooser
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS
, new Parcelable[] { captureIntent });
// On select image call onActivityResult method of activity
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
catch(Exception e){
Toast.makeText(getBaseContext(), "Exception:"+e,
Toast.LENGTH_LONG).show();
}
}
// openFileChooser for Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg){
openFileChooser(uploadMsg, "");
}
//openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType,
String capture) {
openFileChooser(uploadMsg, acceptType);
}
// The webPage has 2 filechoosers and will send a
// console message informing what action to perform,
// taking a photo or updating the file
public boolean onConsoleMessage(ConsoleMessage cm) {
onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
return true;
}
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
//Log.d("androidruntime", "Show console messages, Used for debugging: " + message);
}
}); // End setWebChromeClient
Return here when file selected from camera or from SDcard
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if(requestCode==FILECHOOSER_RESULTCODE)
{
if (null == this.mUploadMessage) {
return;
}
Uri result=null;
try{
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = intent == null ? mCapturedImageURI : intent.getData();
}
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(), "activity :"+e,
Toast.LENGTH_LONG).show();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
Add filepath code for API Level 25.
Related
I created a simple app to taking photos that will store in the SURVEYASSIST folder in the SD card. It is working fine in my mobile Redmi 3s prime (Android version Marshmallow 6.0.1), but when I open the app in Redmi Note 5 pro (Oreo 8.1.0) and click on the camera button, the app is crashing. And error code is attaching as seen in the image.
public class MainActivity extends AppCompatActivity {
public static final int CAPTURE_IMAGE_FULLSIZE_ACTIVITY_REQUEST_CODE = 1777;
private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static final String Demo_ImagePath ="/storage/emulated/0/SURVEYASSIST/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
String imgcurTime = dateFormat.format(new Date());
String _path = Demo_ImagePath + File.separator + imgcurTime + ".jpg";
File file = new File(_path);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(intent, CAPTURE_IMAGE_FULLSIZE_ACTIVITY_REQUEST_CODE);
}
});
}
}
The FileUriExposedException is what is being thrown. From documentation:
The exception that is thrown when an application exposes a file:// Uri to another app.
There's a good article on how to fix this here but I will summarize in case the link goes dead at some point:
Add this to your manifest:
<manifest ...>
<application ...>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
</application>
Create XML file res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
Change your code:
From:
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
To:
Uri apkURI = FileProvider.getUriForFile(
this,
this.getApplicationContext()
.getPackageName() + ".provider", file);
intent.setDataAndType(apkURI, "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
I'm working on maven web application with JSP and Servlets. I have a href which has a applet class call which loads my applet success fully from certain Servlet but on my windows only. Now I want to load this into client browser wherever this link is clicked from remote machine's IP. Please help me on this it should be loaded on client browser from HTML or JSP.
This is my class in my Java:
package com.enidiris.util;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
public class AppletIris extends JApplet {
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean inAnApplet = true;
public AppletIris() {
this(true);
}
public AppletIris(boolean inAnApplet) {
this.inAnApplet = inAnApplet;
if (inAnApplet) {
getRootPane().putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE);
}
}
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public void createGUI() {
MainPanel mainPanel = new MainPanel();
getContentPane().add(mainPanel);
}
}
This is my HTML in same web application but nothing says in console of browsers, not even loading applet.
webapp/ register.jsp
<body onload="checkSession();">
<jsp:plugin type="applet" name="AppletIris" code="com.enidiris.util.AppletIris" width="950" height="650" hspace="0" vspace="0" codebase="." >
</jsp:plugin>
<!-- <object codetype="application/java" classid="java:AppletIris.class" -->
<!-- archive="enidiris-applet.jar" width="740" height="400"></object> -->
</body>
I have my smart banner working, but Google AdMob ad serving has been disabled to my application
Violation explanation
ADS AND CONTENT OVERLAP: As stated in our program policies, publishers are not permitted to alter the behavior of AdMob ads in any way. This includes placing ads so that they cover any part of the application content or parts of an application covering any portion of the ads. If your app has drop-down menus they are not permitted to cover AdMob ads.....
Appreciate any help I can get, thank you!
MainActivity.java
package bb.hoppingbird;
import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.opengl.CCGLSurfaceView;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import android.app.Activity;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v4.view.ViewPager.LayoutParams;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class MainActivity extends Activity {
private CCGLSurfaceView mGLSurfaceView;
//<!-- Admob Ads Using Google Play Services SDK -->
private static final String AD_UNIT_ID = "ca-app-pub-1141613635835332/7968365003";
private static final String AD_INTERSTITIAL_UNIT_ID = "df2cce209f194168";
/** The Admob ad. */
private InterstitialAd interstitialAd = null;
public AdView adView = null;
public static MainActivity app;
public void onCreate(Bundle savedInstanceState)
{
app = this;
super.onCreate(savedInstanceState);
// set view
mGLSurfaceView = new CCGLSurfaceView(this);
//Ads ----------------
// Create the adView
RelativeLayout layout = new RelativeLayout(this);
layout.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
//<!-- Ads Using Google Play Services SDK -->
adView = new AdView(this);
adView.setAdSize(AdSize.SMART_BANNER);
adView.setAdUnitId(AD_UNIT_ID);
// Add the adView to it
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
adView.setLayoutParams(params);
layout.addView(mGLSurfaceView);
layout.addView(adView);
setContentView(layout);
//New AdRequest
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
//-----------------------------------------------------Interstitial Add
// Create an Interstitial ad.
interstitialAd = new InterstitialAd(this);
interstitialAd.setAdUnitId(AD_INTERSTITIAL_UNIT_ID);
interstitialAd.setAdListener(new AdListener() {
#Override
public void onAdLoaded() {
interstitialAd.show();
}
#Override
public void onAdFailedToLoad(int errorCode) {
Toast.makeText(getApplicationContext(), "Interstitial Ads loading failed", Toast.LENGTH_SHORT).show();
}
});
// Load the interstitial ad.
//showInterstitialAds();
//----------------------
// set director
CCDirector director = CCDirector.sharedDirector();
director.attachInView(mGLSurfaceView);
director.setAnimationInterval(1/60);
// get display info
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
G.display_w = displayMetrics.widthPixels;
G.display_h = displayMetrics.heightPixels;
G.scale = Math.max(G.display_w/1280.0f, G.display_h/800.0f);
G.width = G.display_w / G.scale;
G.height = G.display_h / G.scale;
// get data
SharedPreferences sp = CCDirector.sharedDirector().getActivity().getSharedPreferences("GameInfo", 0);
G.music = sp.getBoolean("music", true);
G.sound = sp.getBoolean("sound", true);
// create sound
G.soundMenu = MediaPlayer.create(this, R.raw.menu);
G.soundMenu.setLooping(true);
G.soundGame = MediaPlayer.create(this, R.raw.game);
G.soundGame.setLooping(true);
G.soundCollide = MediaPlayer.create(this, R.raw.collide);
G.soundJump = MediaPlayer.create(this, R.raw.jump);
G.soundLongJump = MediaPlayer.create(this, R.raw.long_jump);
G.soundSpeedDown = MediaPlayer.create(this, R.raw.speed_down);
G.soundSpeedUp = MediaPlayer.create(this, R.raw.speed_up);
G.soundDirection = MediaPlayer.create(this, R.raw.direction_sign);
G.soundClick = MediaPlayer.create(this, R.raw.menu_click);
G.soundCollect = MediaPlayer.create(this, R.raw.collect);
G.bgSound = G.soundMenu;
// show menu
CCScene scene = CCScene.node();
scene.addChild(new MenuLayer(true));
director.runWithScene(scene);
}
#Override
public void onPause()
{
if (adView != null) {
adView.pause();
}
super.onPause();
G.bgSound.pause();
CCDirector.sharedDirector().onPause();
}
#Override
public void onResume()
{
super.onResume();
if (adView != null) {
adView.resume();
}
if( G.music ) G.bgSound.start();
CCDirector.sharedDirector().onResume();
}
#Override
public void onDestroy()
{
// Destroy the AdView.
if (adView != null) {
adView.destroy();
}
super.onDestroy();
G.bgSound.pause();
CCDirector.sharedDirector().end();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
CCDirector.sharedDirector().onKeyDown(event);
return true;
}
return super.onKeyDown(keyCode, event);
}
public void showInterstitialAds()
{
runOnUiThread(new Runnable() {
public void run() {
AdRequest interstitialAdRequest = new AdRequest.Builder().build();
interstitialAd.loadAd(interstitialAdRequest);
}
});
}
}
I'm sorry if this is a stupid question, but my knowledge is very limited on this subject. I'm not asking for a complete solution, I just need a little push in the right direction.
I have an adroid tablet with a lot of pdf files on the sdcard. Now I want to make a tool to scan a barcode with the filename and open this pdf file. The barcode will be like test and the corresponding file will be file://sdcard/pdf/test.pdf.
The barcode scanner gives an enter after scanning so I only need a textbox on screen and the enter key to initiate the opening activity. We'd rather not connect this tablet to our network, so it doesn't have an internet connection.
I have tried to make an app for this, but since my android programming skills are pretty limited this didn't work out for me.
Now I am trying to make a web app with either php or javascript. This isn't really working out for me since my experience with either of those is limited as well. Initially I was thinking of an MS Access tool in VBA but since there's no runtime for Android this idea doesn't work out.
I've already searched for a solution, but haven't found anything I could use.
Does anyone have a suggestion on with kind of tool I should use? And perhaps put me in the right direction. For more information, please ask.
UPDATE 1:
Right now I've created a webpage using HTML and JavaScript. My code is as the following:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=windows-1252" http-equiv="content-type">
<script type="text/javascript">
function clickyClick() {
url = 'file:///mnt/sdcard/documents/instructies/' + document.getElementById("barcode").value + '.pdf'
window.open(url, '_blank');
document.getElementById("barcode").value = "";
}
</script> <title>Open werkinstructie</title>
</head>
<body>
<form><span style="font-weight: bold; font-family: Helvetica,Arial,sans-serif; font-size:50px;">Barcode:</span>
<input autofocus="autofocus" style="font-size:50px" name="barcode" id="barcode"
onkeydown="if (event.which == 13) clickyClick()" type="text"> </form>
<br>
<button style="height: 85px; width: 120px; font-size: 30px; font-weight:bold;"
onclick="clickyClick()">Open</button>
</body>
</html>
Which I open with Firefox on the tablet. For now I can't get it to work using the Barcode scanner or the enter key, the button however does work. This code opens the file in Adobe Reader, which is exactly what I want to do.
UPDATE 2:
MainActivity.java:
package com.example...............;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
final String TAG = null;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText mEditText = (EditText) findViewById(R.id.editText1);
mEditText.setImeActionLabel("Klaar", KeyEvent.KEYCODE_ENTER);
EditText.OnEditorActionListener exampleListener = new EditText.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
Log.d(TAG, "onEditorAction, key=" + event.getKeyCode() + " action=" + event.getAction());
if (event.getAction() == KeyEvent.KEYCODE_ENTER) {
openPDF(mEditText.toString());
mEditText.setText("");
}
return true;
}
};
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, 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();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private void openPDF(String fileName){
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(baseDir + File.separator + "documents/instructies/"+fileName+".pdf");
if (file.exists()) {
Uri path = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(path, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No reader",
Toast.LENGTH_SHORT).show();
}
}
}
}
UPDATE 3:
MainActivity.java
package com.example..............;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
final String LOG_TAG = "MainActivity";
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText mEditText = (EditText) findViewById(R.id.editText1);
mEditText.setImeActionLabel("Klaar", KeyEvent.KEYCODE_ENTER);
mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Log.d(LOG_TAG, "onEditorAction, key=" + event.getKeyCode() + "action =" + event.getAction());
if (event.getAction() ==1 && event.getKeyCode() ==66) {
String Text = mEditText.getText().toString();
openPDF(Text);
mEditText.setText("");
}
return true;
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, 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();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private void openPDF(String fileName) {
File file = new File("sdcard/documents/instructies/" + fileName + ".pdf");
//File file = new File("sdcard/documents/instructies/0195476.pdf");
//final String LOG_TAG_2 = "MainActivity";
//Log.d(LOG_TAG_2, "exist = " + file.exists());
if (file.exists()) {
Uri path = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(path, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No reader",
Toast.LENGTH_SHORT).show();
}
}
}
}
In your layout add an EditText as this:
<EditText
android:id="#+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
>
<requestFocus />
</EditText>
In your activity add this in onCreate method:
final EditText mEditText = (EditText) findViewById(R.id.editText1);
mEditText.setImeActionLabel("Done", KeyEvent.KEYCODE_ENTER);
EditText.OnEditorActionListener exampleListener = new EditText.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
Log.d(TAG, "onEditorAction, key="+event.getKeyCode() + " action="+event.getAction());
if (event.getAction() == KeyEvent.KEYCODE_ENTER) {
openPDF(mEditText.toString());
}
return true;
}
};
To open a PDF, install a PDF app from market and in your activity open PDF as this:
private void openPDF(String fileName){
File file = new File("/sdcard/pdf/"+fileName+".pdf");
if (file.exists()) {
Uri path = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(path, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No reader",
Toast.LENGTH_SHORT).show();
}
}
}
I searched, but I didn't find an answer. I'm developing an Android app based on webview, using HTML5 and JavaScript. Can I call an Android method, like makeToast() from JavaScript?
You can do this by adding a JavaScript Interface to your WebView and exposing specific methods to the JavaScript code running in your web view. In other words, you'll need to wrap the calls to Android's Toast class in a method you create in your activity/fragment.
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView android:id="#+id/web_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView)findViewById(R.id.web_view);
webView.loadUrl("file:///android_asset/web.html");
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewJavaScriptInterface(this), "app");
}
/*
* JavaScript Interface. Web code can access methods in here
* (as long as they have the #JavascriptInterface annotation)
*/
public class WebViewJavaScriptInterface{
private Context context;
/*
* Need a reference to the context in order to sent a post message
*/
public WebViewJavaScriptInterface(Context context){
this.context = context;
}
/*
* This method can be called from Android. #JavascriptInterface
* required after SDK version 17.
*/
#JavascriptInterface
public void makeToast(String message, boolean lengthLong){
Toast.makeText(context, message, (lengthLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT)).show();
}
}
}
assets/web.html
<!DOCTYPE html>
<html>
<head>
<title>JavaScript View</title>
<script type="text/javascript">
function showToast(){
var message = document.getElementById("message").value;
var lengthLong = document.getElementById("length").checked;
/*
Call the 'makeToast' method in the Java code.
'app' is specified in MainActivity.java when
adding the JavaScript interface.
*/
app.makeToast(message, lengthLong);
return false;
}
/*
Call the 'showToast' method when the form gets
submitted (by pressing button or return key on keyboard).
*/
window.onload = function(){
var form = document.getElementById("form");
form.onsubmit = showToast;
}
</script>
</head>
<body>
<form id="form">
Message: <input id="message" name="message" type="text"/><br />
Long: <input id="length" name="length" type="checkbox" /><br />
<input type="submit" value="Make Toast" />
</form>
</body>
</html>
Check out WebView's addJavascriptInterface() method:
http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface%28java.lang.Object,%20java.lang.String%29
Just because it is more convenient (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" />
After creating your Main Activity code you need to create your Javascript code and call WebviewInterface from that, Let's see the example:
public class MainActivity extends AppCompatActivity {
String TAG = "MainActivity";
Context context;
WebView mWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
mWebView = (WebView) findViewById(R.id.webview);
initWebView();
String ENROLLMENT_URL = "file:///android_asset/about_page.html";
mWebView.loadUrl(ENROLLMENT_URL);
}
#SuppressLint({ "SetJavaScriptEnabled" })
private void initWebView() {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.addJavascriptInterface(new WebviewInterface(), "Interface");
}
public class WebviewInterface {
#JavascriptInterface
public void javaMehod(String val) {
Log.i(TAG, val);
Toast.makeText(context, val, Toast.LENGTH_SHORT).show();
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.legendblogs.android.MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/webview"/>
</RelativeLayout>
Look at this link to see full example https://www.legendblogs.com/blog/how-to-call-native-java-methods-from-webview-javascript/121764
In Kotlin You can try below code:
In Youre acitivity/fragment that have wevbview add this code:
binding.webViewTest.loadUrl("youreUrl")
binding.webViewTest.settings.javaScriptEnabled = true
binding.webViewTest.addJavascriptInterface(
WebViewJavaScriptInterface(this), "testApp")
and add this class:
class WebViewJavaScriptInterface(context: Context) {
private val context: Context
/*
* This method can be called from Android. #JavascriptInterface
* required after SDK version 17.
*/
#JavascriptInterface
fun makeToast(message: String?) {
Toast.makeText(
context,
message,
Toast.LENGTH_SHORT
).show()
}
init {
this.context = context
}
}
and add this code in your web page:
function showToast(){
/*
Call the 'makeToast' method in the Kotlin code.
'appTest' is specified in MainActivity/Fragment when
adding the JavaScript interface.
*/
testApp.makeToast("Hello Kotlin :)");
return false;
}