Issue
I have a React Web Application that has a camera and google maps embedded in it. I have hosted this in Netlify and have a link for it.
I'm implementing Android Webviews with the above link in java. I got the camera working but the map isn't loading.
I tried all the ways and saw many StackOverflow questions and I implemented the same but was still not able to load google maps.
Below is the MainActivity.java
code
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.single.PermissionListener;
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
public WebView webView;
GoogleMap mGoogleMap;
public class GeoWebViewClient extends WebViewClient
{
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
// if(url.contains("maps"))
// {
// Uri gmmIntentUri = Uri.parse(url);
// Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
// mapIntent.setPackage("com.google.android.apps.maps");
// if (mapIntent.resolveActivity(getPackageManager()) != null) {
// startActivity(mapIntent);
// }
// return true;
// }
view.loadUrl(url);
return true;
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkMyPermission();
// int PERMISSION_ALL = 1;
// String[] PERMISSIONS = {
// Manifest.permission.ACCESS_FINE_LOCATION,
// Manifest.permission.ACCESS_COARSE_LOCATION,
// Manifest.permission.READ_EXTERNAL_STORAGE,
// android.Manifest.permission.CAMERA
// };
// if( !hasPermissions(this, PERMISSIONS) )
// {
// ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
// }
ActivityResultLauncher<String> cameraPermission = registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if(result)
{
System.out.println("GRANTED");
}
else
{
System.out.println("NOPE");
}
}
});
cameraPermission.launch(Manifest.permission.CAMERA);
ActivityResultLauncher<String> Access_Coarse_location_Permission = registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if(result)
{
System.out.println("GRANTED");
}
else
{
System.out.println("NOPE");
}
}
});
Access_Coarse_location_Permission.launch(Manifest.permission.ACCESS_COARSE_LOCATION);
ActivityResultLauncher<String> Access_Fine_location_Permission = registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if(result)
{
System.out.println("GRANTED");
}
else
{
System.out.println("NOPE");
}
}
});
Access_Fine_location_Permission.launch(Manifest.permission.ACCESS_FINE_LOCATION);
webView = (WebView) findViewById(R.id.webview);
webView.setWebViewClient(new WebViewClient());
webView.setWebViewClient(new GeoWebViewClient());
webView.loadUrl("https://myappurl.netlify.app/");
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setDomStorageEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setAllowFileAccess(true);
webSettings.setSupportZoom(false);
webSettings.setPluginState(WebSettings.PluginState.ON);
webSettings.setMediaPlaybackRequiresUserGesture(false);
webView.setWebChromeClient(new WebChromeClient()
{
@Override
public void onPermissionRequest(final PermissionRequest request)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
request.grant(request.getResources());
}
}
});
}
private void checkMyPermission() {
Dexter.withContext(this).withPermission(Manifest.permission.ACCESS_FINE_LOCATION).withListener(new PermissionListener() {
@Override
public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
Toast.makeText(MainActivity.this, "FINE PERMISSION GRANTED", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
}
@Override
public void onPermissionRationaleShouldBeShown(com.karumi.dexter.listener.PermissionRequest permissionRequest, PermissionToken permissionToken) {
}
}).check();
Dexter.withContext(this).withPermission(Manifest.permission.ACCESS_COARSE_LOCATION).withListener(new PermissionListener() {
@Override
public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
Toast.makeText(MainActivity.this, "COARSE PERMISSION GRANTED", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
}
@Override
public void onPermissionRationaleShouldBeShown(com.karumi.dexter.listener.PermissionRequest permissionRequest, PermissionToken permissionToken) {
}
}).check();
}
@SuppressLint("MissingPermission")
@Override
public void onMapReady(@NonNull GoogleMap googleMap)
{
mGoogleMap = googleMap;
mGoogleMap.setMyLocationEnabled(true);
}
// public static boolean hasPermissions(Context context, String... permissions)
// {
// if( context != null && permissions != null )
// {
// for (String permission : permissions)
// {
// if(ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED)
// {
// return false;
// }
// }
// }
// return true;
// }
@Override
public void onBackPressed(){
if (webView.canGoBack()){
webView.goBack();
}
else{
super.onBackPressed();
}
}
}
Below is the AndroidManifest.xml
code
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.webviewtest">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.webkit.PermissionRequest" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<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/Theme.WebviewTest">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="my gmaps api key" />
</application>
</manifest>
Below is the build.gradle
id 'com.android.application'
}
android {
compileSdk 31
defaultConfig {
applicationId "com.example.webviewtest"
minSdk 19
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.karumi:dexter:6.2.3'
}
Kindly help me in resolving this issue. Thanks in advance.
Solution
Below is the MainActivity.java code
package com.example.file_upload;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.GeolocationPermissions;
import android.webkit.JavascriptInterface;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
WebView webView;
private static final String TAG = MainActivity.class.getSimpleName();
private String mCM;
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> mUMA;
private final static int FCR=1;
private DBHandler dbHandler;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
super.onActivityResult(requestCode, resultCode, intent);
if(Build.VERSION.SDK_INT >= 21){
Uri[] results = null;
//Check if response is positive
if(resultCode== Activity.RESULT_OK){
if(requestCode == FCR){
if(null == mUMA){
return;
}
if(intent == null){
//Capture Photo if no image available
if(mCM != null){
results = new Uri[]{Uri.parse(mCM)};
}
}else{
String dataString = intent.getDataString();
if(dataString != null){
results = new Uri[]{Uri.parse(dataString)};
}
}
}
}
mUMA.onReceiveValue(results);
mUMA = null;
}else{
if(requestCode == FCR){
if(null == mUM) return;
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUM.onReceiveValue(result);
mUM = null;
}
}
}
@SuppressLint({"SetJavaScriptEnabled", "WrongViewCast"})
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(Build.VERSION.SDK_INT >=23 && (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED )) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
webView = (WebView) findViewById(R.id.webview);
assert webView != null;
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setGeolocationEnabled(true);
webSettings.setGeolocationDatabasePath(getFilesDir().getPath());
webSettings.setDatabaseEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setDomStorageEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setAllowFileAccess(true);
webSettings.setAllowContentAccess(true);
webSettings.setSupportZoom(true);
webSettings.setPluginState(WebSettings.PluginState.ON);
webSettings.setMediaPlaybackRequiresUserGesture(false);
webSettings.setAppCacheMaxSize(500*1024*1024);
webSettings.setAppCachePath( getApplicationContext().getCacheDir().getAbsolutePath());
webSettings.setAppCacheEnabled(true);
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
if( !isNetworkAvailable() )
{
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
if(Build.VERSION.SDK_INT >= 21){
webSettings.setMixedContentMode(0);
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}else if(Build.VERSION.SDK_INT >= 19){
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}else if(Build.VERSION.SDK_INT < 19){
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webView.setWebViewClient(new Callback());
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
webView.loadUrl("https://gleeful-druid-15e4b7.netlify.app/");
webView.setWebChromeClient(new WebChromeClient(){
@Override
public void onPermissionRequest(final PermissionRequest request)
{
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP )
{
request.grant(request.getResources());
}
}
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback){
callback.invoke(origin, true, false);
}
//For Android 3.0+
// public void openFileChooser(ValueCallback<Uri> uploadMsg){
// mUM = uploadMsg;
// Intent i = new Intent(Intent.ACTION_GET_CONTENT);
// i.addCategory(Intent.CATEGORY_OPENABLE);
// i.setType("*/*");
// MainActivity.this.startActivityForResult(Intent.createChooser(i,"File Chooser"), FCR);
// }
// For Android 3.0+, above method not supported in some android 3+ versions, in such case we use this
// public void openFileChooser(ValueCallback uploadMsg, String acceptType){
// mUM = uploadMsg;
// Intent i = new Intent(Intent.ACTION_GET_CONTENT);
// i.addCategory(Intent.CATEGORY_OPENABLE);
// i.setType("*/*");
// MainActivity.this.startActivityForResult(
// Intent.createChooser(i, "File Browser"),
// FCR);
// }
//For Android 4.1+
// public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
// mUM = uploadMsg;
// Intent i = new Intent(Intent.ACTION_GET_CONTENT);
// i.addCategory(Intent.CATEGORY_OPENABLE);
// i.setType("*/*");
// MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FCR);
// }
//For Android 5.0+
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams){
if(mUMA != null){
mUMA.onReceiveValue(null);
}
mUMA = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null){
File photoFile = null;
try{
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
}catch(IOException ex){
Log.e(TAG, "Image file creation failed", ex);
}
if(photoFile != null){
mCM = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
}else{
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
if(takePictureIntent != null){
intentArray = new Intent[]{takePictureIntent};
}else{
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
return true;
}
});
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = ( ConnectivityManager ) getSystemService(CONNECTIVITY_SERVICE) ;
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
public class Callback extends WebViewClient{
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
Toast.makeText(getApplicationContext(), "Failed loading app!", Toast.LENGTH_SHORT).show();
}
}
// Create an image file
private File createImageFile() throws IOException{
@SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "img_"+timeStamp+"_";
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
return File.createTempFile(imageFileName,".jpg",storageDir);
}
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event){
if(event.getAction() == KeyEvent.ACTION_DOWN){
switch(keyCode){
case KeyEvent.KEYCODE_BACK:
if(webView.canGoBack()){
webView.goBack();
}else{
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
@Override
public void onBackPressed()
{
if( webView.canGoBack() )
{
webView.goBack();
}
else
{
super.onBackPressed();
}
}
private class WebAppInterface {
Context mContext;
WebAppInterface(Context c){
this.mContext=c;
}
@JavascriptInterface
public void showToast(String jsonData)
{
try
{
JSONObject data = new JSONObject(jsonData);
String name = data.getString("name");
String email = data.getString("email");
long contact_number1 = data.getLong("contact_number1");
long contact_number2 = data.getLong("contact_number2");
long retail_size = data.getLong("retail_size");
String it_automation = data.getString("it_automation");
long no_of_mobile = data.getLong("no_of_mobile");
long no_of_tab = data.getLong("no_of_tab");
long no_of_computer = data.getLong("no_of_computer");
long no_of_printer = data.getLong("no_of_printer");
long no_of_scanner = data.getLong("no_of_scanner");
double latitude = data.getDouble("latitude");
double longitude = data.getDouble("longitude");
String image = data.getString("image");
Toast.makeText(mContext, String.valueOf(latitude) , Toast.LENGTH_LONG).show();
dbHandler = new DBHandler(MainActivity.this);
dbHandler.AddRetailData(name, email, contact_number1, contact_number2, retail_size, it_automation, no_of_mobile, no_of_tab, no_of_computer, no_of_printer, no_of_scanner, latitude, longitude, image );
// Log.d("EMAIL", "value: " + email);
// Log.d("contact number1", "value: " + contact_number1);
// Toast.makeText(mContext, email , Toast.LENGTH_SHORT).show();
Toast.makeText(mContext, String.valueOf(latitude) , Toast.LENGTH_LONG).show();
Toast.makeText(MainActivity.this, "Retail Data has been saved.", Toast.LENGTH_LONG).show();
}
catch (Exception e)
{
}
}
}
}
Below is the AndroidManifest.xml code
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.file_upload">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.webkit.PermissionRequest" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-feature android:name="android.hardware.camera" />
<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/Theme.File_Upload">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
This worked for me.
Answered By - sureshtechi
Answer Checked By - Marilyn (JavaFixing Volunteer)