OpenCV app - Android camera keeps showing black screen - java

Background
I had started out this project on Android Studio with the intent of creating a OpenCV application that could process frames using the camera on my OnePlus android device. After running the application, I was gratified to see it finally launch on my device. However, the application shows up with a black screen where the camera preview should be. Here is my code for my MainActivity, activity_main, and AndroidManifest files:
EDIT: While the application launched on my device, I did give the application permission to use camera
MainActivity.java
package com.example.cv;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2
{
private static String TAG = "MainActivity";
JavaCameraView javaCameraView;
Mat mRGBA, mRGBAT;
BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(MainActivity.this) {
#Override
public void onManagerConnected(int status)
{
if (status == BaseLoaderCallback.SUCCESS) {
javaCameraView.enableView();
} else {
super.onManagerConnected(status);
}
}
};
static
{
if (OpenCVLoader.initDebug())
{
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
}
else
{
Log.d(TAG, "OpenCV not Working or Loaded.");
}
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
javaCameraView.setVisibility(SurfaceView.VISIBLE);
javaCameraView.setCvCameraViewListener(MainActivity.this);
}
#Override
public void onCameraViewStarted(int width, int height)
{
mRGBA = new Mat(height, width, CvType.CV_8UC4);
}
#Override
public void onCameraViewStopped()
{
mRGBA.release();
}
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
{
mRGBA = inputFrame.rgba();
mRGBAT = mRGBA.t();
Core.flip(mRGBA.t(), mRGBAT, 1);
Imgproc.resize(mRGBAT, mRGBAT, mRGBA.size());
return mRGBAT;
}
#Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
#Override
protected void onDestroy() {
super.onDestroy();
if (javaCameraView != null)
{
javaCameraView.disableView();
}
}
#Override
protected void onPause() {
super.onPause();
if (javaCameraView != null)
{
javaCameraView.disableView();
}
}
#Override
protected void onResume() {
super.onResume();
if (OpenCVLoader.initDebug())
{
Log.d(TAG, "OpenCV is Configured or Connected successfully.");
baseLoaderCallback.onManagerConnected(BaseLoaderCallback.SUCCESS);
}
else
{
Log.d(TAG, "OpenCV not Working or Loaded.");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
}
}
}
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=".MainActivity">
<org.opencv.android.JavaCameraView
android:id="#+id/my_camera_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cv">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-feature android:name="android.hardware.camera.front"/>
<uses-feature android:name="android.hardware.camera.front.autofocus"/>
<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=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

The issue you are facing is actually with the camera permission check
Starting from Android Marshmallow, the CAMERA permission is considered a dangerous one and the user must explicitly agree on its usage at runtime, clicking on the system dialog
This wouldn't happen for "normal permissions" [e.g. INTERNET] that are granted by default. If you are interested in which ones are dangerous and which not you can check for the one of interest directly in the Android documentation
What's happening with your initial code is that you are mentioning you will require the camera permission in your manifest file and then you are enabling it from the Android Settings [toggling the Camera slider]. But then, when you start the app there is nothing in the code that goes and checks that toggle. Then you get a black screen because Android assumes that the user has not given explicit consent
This link from the Android documentation should help you understand more. Here the snippet that will make your code work. In a nutshell, with onCreate() you go and check if the user has already granted the permission and if not you will ask it. In onRequestPermissionResult() there is the check we were talking about before. If the user agreed the camera will start, otherwise it won't
private static final int MY_CAMERA_REQUEST_CODE = 100;
int activeCamera = CameraBridgeViewBase.CAMERA_ID_FRONT;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.my_camera_view);
// checking if the permission has already been granted
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Permissions granted");
initializeCamera(javaCameraView, activeCamera);
} else {
// prompt system dialog
Log.d(TAG, "Permission prompt");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_CAMERA_REQUEST_CODE);
}
}
// callback to be executed after the user has givenapproval or rejection via system prompt
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_CAMERA_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// camera can be turned on
Toast.makeText(this, "camera permission granted", Toast.LENGTH_LONG).show();
initializeCamera(javaCameraView, activeCamera);
} else {
// camera will stay off
Toast.makeText(this, "camera permission denied", Toast.LENGTH_LONG).show();
}
}
}
private void initializeCamera(JavaCameraView javaCameraView, int activeCamera){
javaCameraView.setCameraPermissionGranted();
javaCameraView.setCameraIndex(activeCamera);
javaCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
}

Related

Right way of requesting permissions for location coordinates in Android 10

Below code does not work on Android 10 until you manually enable the location from settings. When I go through the Android 10 documentation, it says if you are fetching location from the visible activity, if that was the case you don't need any foreground service, that's the reason I haven't used any service here.
And also as per Android 10 guidelines, after permission getting granted I am calling getLastLocation function.
Implementation is like, on main activity I am fetching location coordinates.
Please help, where in the code am I going wrong?
MainActivity.java
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.NonNull;
import com.google.android.material.snackbar.Snackbar;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private FusedLocationProviderClient mFusedLocationClient;
protected Location mLastLocation;
private String mLatitudeLabel;
private String mLongitudeLabel;
private TextView mLatitudeText;
private TextView mLongitudeText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
mLatitudeLabel = getResources().getString(R.string.latitude_label);
mLongitudeLabel = getResources().getString(R.string.longitude_label);
mLatitudeText = (TextView) findViewById((R.id.latitude_text));
mLongitudeText = (TextView) findViewById((R.id.longitude_text));
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
#SuppressWarnings("MissingPermission")
private void getLastLocation() {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(this, new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLastLocation = task.getResult();
Log.d("raviraj", "lat is " +mLastLocation.getLatitude());
Log.d("raviraj", "long is " +mLastLocation.getLongitude());
mLatitudeText.setText(String.format(Locale.ENGLISH, "%s: %f",
mLatitudeLabel,
mLastLocation.getLatitude()));
mLongitudeText.setText(String.format(Locale.ENGLISH, "%s: %f",
mLongitudeLabel,
mLastLocation.getLongitude()));
} else {
Log.w("RAVIRAJ", "getLastLocation:exception" +task.getException());
showSnackbar(getString(R.string.no_location_detected));
}
}
});
}
private void showSnackbar(final String text) {
View container = findViewById(R.id.main_activity_container);
if (container != null) {
Snackbar.make(container, text, Snackbar.LENGTH_LONG).show();
}
}
private void showSnackbar(final int mainTextStringId, final int actionStringId,
View.OnClickListener listener) {
Snackbar.make(findViewById(android.R.id.content),
getString(mainTextStringId),
Snackbar.LENGTH_INDEFINITE)
.setAction(getString(actionStringId), listener).show();
}
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private void startLocationPermissionRequest() {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
showSnackbar(R.string.permission_rationale, android.R.string.ok,
new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
startLocationPermissionRequest();
}
});
} else {
Log.i(TAG, "Requesting permission");
startLocationPermissionRequest();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted.
getLastLocation();
} else {
showSnackbar(R.string.permission_denied_explanation, R.string.settings,
new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
}
}
}
}
Manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.gms.location.sample.basiclocationsample" >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/Theme.Base" >
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".NewMain"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Well there is a problem. Official documentation states that it can be null in some cases
https://developer.android.com/training/location/retrieve-current
Location is turned off in the device settings. The result could be null even if the last location was previously retrieved because disabling location also clears the cache.
The device never recorded its location, which could be the case of a new device or a device that has been restored to factory settings.
Google Play services on the device has restarted, and there is no active Fused Location Provider client that has requested location after the services restarted. To avoid this situation you can create a new client and request location updates yourself. For more information, see Receiving Location Updates.
In your case you can get location only if GPS is ON. Even for last known location.
https://developer.android.com/training/location/retrieve-current#last-known
Please add this permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
and add this to application tag
android:usesCleartextTraffic="true"

Android OpenCV camera example is just showing black screen

I've spent days (well, nights) trying to work this out. So many examples online are for different versions of Android Studio, different versions of Android, different versions of OpenCV and I can't get any of them to the final 'working' stage.
This example (based on a youtube tutorial, I got to the point where I needed permissions. That's fine, I added that in and a check for them, and it pops up asking the user for camera permissions. But the screen stays blank. I've put in logcat debug, all the right methods seem to be getting called. Would appreciate any assistance.
Code:
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mytestopencvapp" >
<uses-permission android:name="android.permission.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/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package com.example.mytestopencvapp;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCamera2View;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {
private static String TAG = "MainActivity";
JavaCameraView javaCameraView;
Mat mRGBA, mRGBAT;
private final int PERMISSIONS_READ_CAMERA=1;
BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(MainActivity.this) {
#Override
public void onManagerConnected(int status) {
Log.d(TAG, "callbacksuccess");
switch (status)
{
case BaseLoaderCallback.SUCCESS:
{
Log.d(TAG, "case success");
javaCameraView.enableView();
break;
}
default:
{
Log.d(TAG, "case default");
super.onManagerConnected(status);
break;
}
}
}
};
static
{
if (OpenCVLoader.initDebug())
{
Log.d(TAG, "OpenCV is intialised");
}
else
{
Log.d(TAG, "OpenCV is not initialised");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView)findViewById(R.id.my_camera_view);
javaCameraView.setVisibility(SurfaceView.VISIBLE);
javaCameraView.setCvCameraViewListener(MainActivity.this);
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
PERMISSIONS_READ_CAMERA);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
Log.d(TAG, "PERMISSIOns granted");
// Permission has already been granted
}
}
#Override
public void onCameraViewStarted(int width, int height) {
Log.d(TAG, "onCameraViewStarted");
mRGBA = new Mat(height, width, CvType.CV_8UC4);
}
#Override
public void onCameraViewStopped() {
Log.d(TAG, "onCameraViewStopped");
mRGBA.release();
}
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
{
Log.d(TAG, "onCameraFrame");
/* mRGBA = inputFrame.rgba();
mRGBAT = mRGBA.t();
Core.flip(mRGBA.t(), mRGBAT, 1);
Imgproc.resize(mRGBAT, mRGBAT, mRGBA.size());
return mRGBAT;*/
mRGBA = inputFrame.rgba();
Core.transpose(mRGBA, mRGBAT);
Imgproc.resize(mRGBAT, mRGBAT, mRGBAT.size(),0,0,0);
Core.flip(mRGBA.t(), mRGBA, 1);
return mRGBA;
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
if (javaCameraView != null)
{
javaCameraView.disableView();
}
}
#Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
if (javaCameraView != null)
{
javaCameraView.disableView();
}
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
if (OpenCVLoader.initDebug())
{
Log.d(TAG, "OpenCV is intialised again");
baseLoaderCallback.onManagerConnected((BaseLoaderCallback.SUCCESS));
}
else
{
Log.d(TAG, "OpenCV is not working");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
}
}
}
And my res layout activity_main.xml file:
<?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=".MainActivity">
<org.opencv.android.JavaCameraView
android:id="#+id/my_camera_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</RelativeLayout>
As far as I can tell, I've linked them all correctly, I can confirm OpenCV initialises, permissions are checked and granted, but then...the JavaCameraView is just black.
You need to tell the CameraView that the camera permission was granted. You can do so by calling the setCameraPermissionGranted() function. This function call should go into the 'permission granted' block in your onCreate method as shown below:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// More code here ...
} else {
Log.d(TAG, "Permissions granted");
javaCameraView.setCameraPermissionGranted();
}
In addition you propably want call this function in onRequestPermissionsResult() for the case where the permission is not granted already. The onRequestPermissionsResult() function is located in your Activity class. It is called when the user granted or denied on of the permission requests the app made.
This could look as follows:
#Override
public void onRequestPermissionsResult(
int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
// Ensure that this result is for the camera permission request
if (requestCode == PERMISSIONS_READ_CAMERA) {
// Check if the request was granted or denied
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// The request was granted -> tell the camera view
javaCameraView.setCameraPermissionGranted();
} else {
// The request was denied -> tell the user and exit the application
Toast.makeText(this, "Camera permission required.",
Toast.LENGTH_LONG).show();
this.finish();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
For more information about the permission system on Android have a look at the following resources:
Request App Permissions This site describes the process of requesting permissions with all necessary steps.
Permissions Overview This site has information about uses-feature in the manifest file. You probably want to add the following line to your manifest file: <uses-feature android:name="android.hardware.camera" android:required="true" /> to prevent the installation on devices without camera.
Now you'll see, that the onCameraFrame callback function is actually invoked. This will lead to a NullPointerException because mRGBAT is not initialized. To just see the camera image you can return inputFrame.rgba() directly in this function. This will at least show the camera image. All further steps are normal image processing to rotate / mirror the image.
I had the same problem in android 10. After a lot of trial and errors, I found that the following method must be implemented in the main activity:
#Override
protected List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}

Android Studio: geolocation not working. Failing to get geolocation in Webview

I'm using a WebView to show different html pages. In these i'm implementing a map from mapbox, but it won't find the users location.
Even if I open up different https pages in the WebView the location will never be found on these sides.
I allowed location permissions and added different user-permissions to the Android Manifest.
These pages locate the location of my devices fine, if i dont open them up from the WebView in my app im working on.
The page i created myself includes a map which works with the api of leaflet.com.
This is some simple Code im trying to make it work with.
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
mTextMessage.setText(R.string.title_home);
return true;
case R.id.navigation_dashboard:
mTextMessage.setText(R.string.title_dashboard);
return true;
case R.id.navigation_notifications:
mTextMessage.setText(R.string.title_notifications);
return true;
}
return false;
}
};
private WebView mWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView myWebView = (WebView) findViewById(R.id.map);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setGeolocationEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setGeolocationEnabled(true);
//myWebView.loadUrl("file:///android_asset/leaflet.html");
myWebView.loadUrl("https://mylocation.org");
mTextMessage = (TextView) findViewById(R.id.message);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
}
And this is the Code of the manifest.xml where i gave different user-permissions
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.hsos.spotit">
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<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=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I hope someone can help me with that, because im spending hours to figure this out. There are nearly no questions in the internet which come close to this or any solution.
Greetings
You need to set up a WebChromeClient method to request the appropriate permissions:
private String geolocationOrigin;
private GeolocationPermissions.Callback geolocationCallback;
WebSettings webSettings = webView.getSettings();
//required for Geolocation
webSettings.setGeolocationEnabled(true);
webView.setWebChromeClient(new MyGeoWebChromeClient());
public class MyGeoWebChromeClient extends WebChromeClient {
#Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
// Geolocation permissions coming from this app's Manifest will only be valid for devices with
// API_VERSION < 23. On API 23 and above, we must check for permissions, and possibly
// ask for them.
String perm = Manifest.permission.ACCESS_FINE_LOCATION;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
ContextCompat.checkSelfPermission(MainActivity.this, perm) == PackageManager.PERMISSION_GRANTED) {
// we're on SDK < 23 OR user has already granted permission
callback.invoke(origin, true, false);
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, perm)) {
// ask the user for permission
ActivityCompat.requestPermissions(MainActivity.this, new String[]{perm}, REQUEST_FINE_LOCATION);
// we will use these when user responds
geolocationOrigin = origin;
geolocationCallback = callback;
}
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_FINE_LOCATION:
boolean allow = false;
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// user has allowed this permission
allow = true;
}
if (geolocationCallback != null) {
// call back to web chrome client
geolocationCallback.invoke(geolocationOrigin, allow, false);
}
break;
}
}

Activities lifecycle after pushed android button "recents" create new activity but dont destroy old activity

When i push android button "recents" and select my app again to go back to de app, the lifecycle of MapsActivity execute onCreate again and create a new instance of MapsActivity.
That´s ok, but the old MapsActivity still running in background and that´s a problem because both MapsActivity have counters and still running and counting with different values in the same variables, like independent new variables.
The counter is in onLocationChanged() and called as "clock".
onLocationChanged() update automaticaly ever 5 seconds.
example:
counter is running : 1, 2, 3, 4, 5, ...
at this point i push android button "recent" (the system execute onCreate)
counter still running like that : 6, 1, 7, 2, 8, 3, ...
at this point a push again android button "recent" (execute onCreate)
counter still running like that : 9, 4, 1, 10, 5, 2, 11, 6, 3, ...
I suppose the lifecycle of MapsActivity not destroy old activities.
I need only one sequencial counter running, and i guess the old MapsActivity must die :)
any idea to solve my problem ? Thanks a lot!
below the manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sasse.myapplication">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".MapsActivity"
android:label="#string/title_activity_maps">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
layout activity_maps.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.sasse.myapplication.MapsActivity" />
Maps_activity
package com.example.sasse.myapplication;
import android.*;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
public class MapsActivity extends FragmentActivity implements LocationListener, ConnectionCallbacks, OnConnectionFailedListener, OnMapReadyCallback {
private static final String TAG = "MapsActivity";
private GoogleMap mMap;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
public int clock = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkLocationPermission();
}
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
//Initialize Google Play Services
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
buildGoogleApiClient();
mMap.setMyLocationEnabled(true);
}
} else {
buildGoogleApiClient();
mMap.setMyLocationEnabled(true);
}
}
public void onLocationChanged(Location location) {
clock++;
if (clock>=10) {
Toast.makeText(this, "clock : "+clock, Toast.LENGTH_SHORT).show();
clock=0;
}
Log.i(TAG, "= = = = = >>> clock : "+clock);
}
// ---------------------------------------------------------------------------------------------
// ---------------- VERIFICAÇÃO DA CONEXÃO COM O SERVIDOR DO GOOGLE MAPS-----------------------
// ---------------------------------------------------------------------------------------------
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public boolean checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Asking user if explanation is needed
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
//Prompt the user once explanation has been shown
ActivityCompat.requestPermissions(this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
public void onRequestPermissionsResult(int requestCode, #NonNull String permissions[], #NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted. Do the
// contacts-related task you need to do.
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (mGoogleApiClient == null) {
buildGoogleApiClient();
}
mMap.setMyLocationEnabled(true);
}
} else {
// Permission denied, Disable the functionality that depends on this permission.
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
}
}
// other 'case' lines to check for other permissions this app might request.
// You can add here other case statements according to your requirement.
}
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
public void onConnected(Bundle bundle) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(5000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
public void onConnectionSuspended(int i) {
}
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
}
Try using launchMode="singleTop" for activity

Allowing microphone access(permission) in WebView [Android Studio] [Java]

I am currently working on a music tuner that uses html5 and a webview to display the 'application'. I've written the all the permission required in manifest and I think for webview there's another permission required.
I am using this https://jbergknoff.github.io/guitar-tuner/ as a sample redirect page for now
Here's my manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.raynordev.projectrosin">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MICROPHONE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.audio.low_latency" />
<uses-feature android:name="android.hardware.audio.pro" />
<uses-feature android:name="android.hardware.microphone" android:required="true"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="#style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.raynordev.projectrosin.HomeActivity">
</activity>
</application>
</manifest>
Here's my .java
public class HomeActivity extends AppCompatActivity {
private WebView wv;
private String TAG = "HomeActivity";
private static final int REQUEST_INTERNET = 200;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
WebView wv = (WebView) findViewById(R.id.webView);
wv.getSettings().setJavaScriptEnabled(true);
wv.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
wv.setWebViewClient(new WebViewClient());
wv.setWebChromeClient(new WebChromeClient());
wv.loadUrl("https://jbergknoff.github.io/guitar-tuner/");
}
}
If require more information from me, please let me know.
Thank you everyone!!
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
Example:
MainActivity
public class MainActivity extends AppCompatActivity {
private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 101;
private ActivityMainBinding mBinding;
private PermissionRequest myRequest;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
setWebView();
}
private void setWebView() {
mBinding.webView.getSettings().setJavaScriptEnabled(true);
mBinding.webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mBinding.webView.setWebViewClient(new WebViewClient());
mBinding.webView.getSettings().setSaveFormData(true);
mBinding.webView.getSettings().setSupportZoom(false);
mBinding.webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
mBinding.webViemBinding.webView.getSettings().setPluginState(WebSettings.PluginState.ON);
mBinding.webView.setWebChromeClient(new WebChromeClient() {
#Override
public void onPermissionRequest(final PermissionRequest request) {
myRequest = request;
for (String permission : request.getResources()) {
switch (permission) {
case "android.webkit.resource.AUDIO_CAPTURE": {
askForPermission(request.getOrigin().toString(), Manifest.permission.RECORD_AUDIO, MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
break;
}
}
}
}
});
mBinding.webView.loadUrl("<your url");
}
#Override
public void onBackPressed() {
if (mBinding.webView.canGoBack()) {
mBinding.webView.goBack();
} else {
super.onBackPressed();
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_RECORD_AUDIO: {
Log.d("WebView", "PERMISSION FOR AUDIO");
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
myRequest.grant(myRequest.getResources());
mBinding.webView.loadUrl("<your url>");
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
}
// other 'case' lines to check for other
// permissions this app might request
}
}
public void askForPermission(String origin, String permission, int requestCode) {
Log.d("WebView", "inside askForPermission for" + origin + "with" + permission);
if (ContextCompat.checkSelfPermission(getApplicationContext(),
permission)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
permission)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{permission},
requestCode);
}
} else {
myRequest.grant(myRequest.getResources());
}
}
}
Mainfest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MICROPHONE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.audio.low_latency" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.audio.pro" />
<uses-feature android:name="android.hardware.microphone"/>
Build.gradle:
android {
compileSdkVersion 26
defaultConfig {
applicationId "myapp.example.com.myapplication"
minSdkVersion 21
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
Summarising, you need the following components:
The MODIFY_AUDIO_SETTINGS permission.
A WebChromeClient and listen to callbacks for onPermissionRequest
Inside this callback, check for the the resources coming in the request object, create a global variable of this request object, iterate and look for specific permission inside the PermissionRequest class, ex. PermissionRequest.RESOURCE_VIDEO_CAPTURE which translates to the Manifest.permission.CAMERA permission, check if your app has this permission or not, using any mechanism you wish if not, request, if yes do 4.
In the permission callback or if the permission is granted, use the request object to grant the permission ex. request.grant(new String[]{PermissionRequest.RESOURCE_VIDEO_CAPTURE}) and you're good to go.
snippet:
webView.webChromeClient = object: WebChromeClient(){
override fun onPermissionRequest(request: PermissionRequest?) {
super.onPermissionRequest(request)
webkitPermissionRequest = request
request?.resources?.forEach {
when(it){
PermissionRequest.RESOURCE_AUDIO_CAPTURE-> {
askForWebkitPermission(it, Manifest.permission.RECORD_AUDIO, REQUEST_CODE_PERMISSION_AUDIO)
}
PermissionRequest.RESOURCE_VIDEO_CAPTURE->{
askForWebkitPermission(it, Manifest.permission.CAMERA, REQUEST_CODE_PERMISSION_CAMERA)
}
}
}
}
}
private fun askForWebkitPermission(webkitPermission: String, androidPermission: String, requestCode: Int){
val context = activity?: return
if (context.hasPermission(androidPermission)){
webkitPermissionRequest!!.grant(arrayOf(webkitPermission))
}else{
requestPermissions(arrayOf(androidPermission), requestCode)
}
}
Hope this helps.
Google's sample for the same: https://github.com/googlesamples/android-PermissionRequest
its problems for reload the webview , after too much research , found this code and create this below code :-
mwebView.setWebChromeClient(new WebChromeClient() {
#Override
public void onPermissionRequest(final PermissionRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
myRequest = request;
for (String permission : request.getResources()) {
if (permission.equals("android.webkit.resource.AUDIO_CAPTURE")) {
demandForPermission(request.getOrigin().toString(), Manifest.permission.RECORD_AUDIO, MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
} else {
myRequest.grant(request.getResources());
}
}
}
}
#Override
public void onPermissionRequestCanceled(PermissionRequest request) {
super.onPermissionRequestCanceled(request);
}
more info in this link Android Webview

Categories