I want to have MapView inside my Fragment
This is my FragmentLayout xml file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#17df0d"
android:orientation="vertical" >
<com.google.android.gms.maps.MapView
android:id="#+id/mapview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/textView1"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="70dp" >
</com.google.android.gms.maps.MapView>
</RelativeLayout>
My AndroidManifest.xml file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="a.b.c.d"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="your_api_key" />
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<permission
android:name="a.b.c.d.permission.MAPS_RECEIVE"
android:protectionLevel="signature" />
<uses-permission android:name="a.b.c.d.permission.MAPS_RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
and my Fragment class
public class ReportFragment extends Fragment implements LocationListener {
MapView mapView = null; //eventually it is being read from view and assigned
when I launch the app, I do not see any map view in my Fragment
From Josh Holtz's example on GitHub:
You should add MapView in your Layout like
<com.google.android.gms.maps.MapView
android:id="#+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
and implement your Fragment like
public class SomeFragment extends Fragment {
MapView mapView;
GoogleMap map;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.some_layout, container, false);
// Gets the MapView from the XML layout and creates it
mapView = (MapView) v.findViewById(R.id.mapview);
mapView.onCreate(savedInstanceState);
// Gets to GoogleMap from the MapView and does initialization stuff
map = mapView.getMap();
map.getUiSettings().setMyLocationButtonEnabled(false);
map.setMyLocationEnabled(true);
// Needs to call MapsInitializer before doing any CameraUpdateFactory calls
try {
MapsInitializer.initialize(this.getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
// Updates the location and zoom of the MapView
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(new LatLng(43.1, -87.9), 10);
map.animateCamera(cameraUpdate);
return v;
}
#Override
public void onResume() {
mapView.onResume();
super.onResume();
}
#Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
}
Adding to M D's answer:
From documentation:
A GoogleMap must be acquired using getMapAsync(OnMapReadyCallback). The MapView automatically initializes the maps system and the view.
According to this, the more correct way to initialize GoogleMap is using getMapAsync.
Note that your class has to implement OnMapReadyCallback
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map_page, container, false);
mMapView = (MapView) v.findViewById(R.id.map_view);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(this); //this is important
return v;
}
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.getUiSettings().setZoomControlsEnabled(true);
mGoogleMap.addMarker(new MarkerOptions().position(/*some location*/));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(/*some location*/, 10));
}
#Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
#Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
In case somebody is looking for a Kotlin version of MapView Fragment ;)
class MapViewKotlinFragment : Fragment(), OnMapReadyCallback {
private var mMap: MapView? = null
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
mMap?.onSaveInstanceState(outState)
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_map, container, false)
mMap = view?.findViewById(R.id.mapViewPlaces) as MapView
mMap?.onCreate(savedInstanceState)
mMap?.getMapAsync(this)
return view
}
override fun onResume() {
super.onResume()
mMap?.onResume()
}
override fun onPause() {
super.onPause()
mMap?.onPause()
}
override fun onStart() {
super.onStart()
mMap?.onStart()
}
override fun onStop() {
super.onStop()
mMap?.onStop()
}
override fun onDestroy() {
super.onDestroy()
mMap?.onDestroy()
}
override fun onLowMemory() {
super.onLowMemory()
mMap?.onLowMemory()
}
override fun onMapReady(googleMap: GoogleMap) {
googleMap.addMarker(MarkerOptions().position(LatLng(0.0, 0.0)).title("Marker"))
}
Old post, but might help someone who is looking to implement maps inside the fragment. Below is the detailed example using kotlin:
First, add the permission in AndroidManifest.xml file:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Then under the application tag add meta-data in AndroidManifest.xml file:
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/map_api_key"/>
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
*To get api link visit: https://developers.google.com/maps/documentation/android-sdk/get-api-key
Implement play services in build.gradle file:
implementation "com.google.android.gms:play-services-location:15.0.1"
implementation "com.google.android.gms:play-services-places:15.0.1"
implementation 'com.google.android.libraries.places:places:2.3.0'
Add pulgin: apply plugin: 'com.google.gms.google-services'
Add maps fragment inside your fragment.xml file:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
In your fragment.kt file, implement OnMapReadyCallback. Initialize the google map using getMapAsync(). Below code will let you integrate the google map and point it to your current location.
package "Your_Package_Name"
import android.Manifest
import android.content.ContentValues.TAG
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.core.content.ContextCompat
import androidx.appcompat.app.AlertDialog
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import com.example.utilitybox.R
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.maps.*
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest
import com.google.android.libraries.places.api.net.PlacesClient
class FragmentHome : Fragment(), OnMapReadyCallback {
private var v:View?=null
private var locationPermissionGranted = false
private var googleMap: GoogleMap?=null
private var fusedLocationProviderClient: FusedLocationProviderClient?=null
private val defaultLocation = LatLng(-33.8523341, 151.2106085)
private var cameraPosition: CameraPosition? = null
private var placesClient: PlacesClient?=null
private var lastKnownLocation: Location? = null
private var likelyPlaceNames: Array<String?> = arrayOfNulls(0)
private var likelyPlaceAddresses: Array<String?> = arrayOfNulls(0)
private var likelyPlaceAttributions: Array<List<*>?> = arrayOfNulls(0)
private var likelyPlaceLatLngs: Array<LatLng?> = arrayOfNulls(0)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (savedInstanceState != null) {
lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
cameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION)
}
v = inflater.inflate(R.layout.fragment_fragment_home, container, false)
if(getString(R.string.map_api_key).isEmpty()){
Toast.makeText(activity, "Add your own API key in MapWithMarker/app/secure.properties as MAPS_API_KEY=YOUR_API_KEY", Toast.LENGTH_LONG).show()
}
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment
mapFragment?.getMapAsync(this)
getLocationPermission()
return v
}
override fun onSaveInstanceState(outState: Bundle) {
googleMap?.let { map ->
outState.putParcelable(KEY_CAMERA_POSITION, map.cameraPosition)
outState.putParcelable(KEY_LOCATION, lastKnownLocation)
}
super.onSaveInstanceState(outState)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.option_get_place) {
showCurrentPlace()
}
return true
}
override fun onMapReady(googleMap: GoogleMap) {
this.googleMap = googleMap
this.googleMap?.setInfoWindowAdapter(object : GoogleMap.InfoWindowAdapter {
override fun getInfoWindow(arg0: Marker): View? {
return null
}
override fun getInfoContents(marker: Marker): View {
val infoWindow = layoutInflater.inflate(R.layout.custom_info_contents,
v?.findViewById(R.id.map), false)
val title = infoWindow.findViewById<TextView>(R.id.title)
title.text = marker.title
val snippet = infoWindow.findViewById<TextView>(R.id.snippet)
snippet.text = marker.snippet
return infoWindow
}
})
getLocationPermission()
updateLocationUI()
getDeviceLocation()
}
private fun getLocationPermission() {
if (ContextCompat.checkSelfPermission(this.requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationPermissionGranted = true
} else {
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION)
}
}
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
locationPermissionGranted = false
when (requestCode) {
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION -> {
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
locationPermissionGranted = true
}
}
}
updateLocationUI()
}
private fun showCurrentPlace() {
if (googleMap == null) {
return
}
if (locationPermissionGranted) {
val placeFields = listOf(Place.Field.NAME, Place.Field.ADDRESS, Place.Field.LAT_LNG)
val request = FindCurrentPlaceRequest.newInstance(placeFields)
val placeResult = placesClient?.findCurrentPlace(request)
placeResult?.addOnCompleteListener { task ->
if (task.isSuccessful && task.result != null) {
val likelyPlaces = task.result
val count = if (likelyPlaces != null && likelyPlaces.placeLikelihoods.size < M_MAX_ENTRIES) {
likelyPlaces.placeLikelihoods.size
} else {
M_MAX_ENTRIES
}
var i = 0
likelyPlaceNames = arrayOfNulls(count)
likelyPlaceAddresses = arrayOfNulls(count)
likelyPlaceAttributions = arrayOfNulls<List<*>?>(count)
likelyPlaceLatLngs = arrayOfNulls(count)
for (placeLikelihood in likelyPlaces?.placeLikelihoods ?: emptyList()) {
likelyPlaceNames[i] = placeLikelihood.place.name
likelyPlaceAddresses[i] = placeLikelihood.place.address
likelyPlaceAttributions[i] = placeLikelihood.place.attributions
likelyPlaceLatLngs[i] = placeLikelihood.place.latLng
i++
if (i > count - 1) {
break
}
}
openPlacesDialog()
} else {
Log.e(TAG, "Exception: %s", task.exception)
}
}
} else {
Log.i(TAG, "The user did not grant location permission.")
googleMap?.addMarker(MarkerOptions()
.title(getString(R.string.default_info_title))
.position(defaultLocation)
.snippet(getString(R.string.default_info_snippet)))
getLocationPermission()
}
}
private fun updateLocationUI() {
if (googleMap == null) {
return
}
try {
if (locationPermissionGranted) {
googleMap?.isMyLocationEnabled = true
googleMap?.uiSettings?.isMyLocationButtonEnabled = true
} else {
googleMap?.isMyLocationEnabled = false
googleMap?.uiSettings?.isMyLocationButtonEnabled = false
lastKnownLocation = null
getLocationPermission()
}
} catch (e: SecurityException) {
Log.e("Exception: %s", e.message, e)
}
}
private fun getDeviceLocation() {
try {
if (locationPermissionGranted) {
val locationResult = fusedLocationProviderClient?.lastLocation
if (locationResult != null) {
locationResult.addOnCompleteListener(this.requireActivity()) { task ->
if (task.isSuccessful) {
// Set the map's camera position to the current location of the device.
lastKnownLocation = task.result
if (lastKnownLocation != null) {
googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(
LatLng(lastKnownLocation!!.latitude,
lastKnownLocation!!.longitude), DEFAULT_ZOOM.toFloat()))
}
} else {
Log.d(TAG, "Current location is null. Using defaults.")
Log.e(TAG, "Exception: %s", task.exception)
googleMap?.moveCamera(CameraUpdateFactory
.newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat()))
googleMap?.uiSettings?.isMyLocationButtonEnabled = false
}
}
}
}
} catch (e: SecurityException) {
Log.e("Exception: %s", e.message, e)
}
}
private fun openPlacesDialog() {
val listener = DialogInterface.OnClickListener { dialog, which ->
val markerLatLng = likelyPlaceLatLngs[which]
var markerSnippet = likelyPlaceAddresses[which]
if (likelyPlaceAttributions[which] != null) {
markerSnippet = """
$markerSnippet
${likelyPlaceAttributions[which]}
""".trimIndent()
}
googleMap?.addMarker(MarkerOptions()
.title(likelyPlaceNames[which])
.position(markerLatLng!!)
.snippet(markerSnippet))
googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(markerLatLng,
DEFAULT_ZOOM.toFloat()))
}
AlertDialog.Builder(this.requireContext())
.setTitle(R.string.pick_place)
.setItems(likelyPlaceNames, listener)
.show()
}
companion object {
private const val DEFAULT_ZOOM = 15
private const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1
private const val KEY_CAMERA_POSITION = "camera_position"
private const val KEY_LOCATION = "location"
private const val M_MAX_ENTRIES = 5
}
}
Once done add custom_info_contents.xml file in layout folder:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:orientation="vertical">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#ff000000"
android:textStyle="bold" />
<TextView
android:id="#+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff7f7f7f" />
</LinearLayout>
Add below code in strings.xml file:
<string name="map_api_key">YOUR_API_KEY</string>
<!--Map strings-->
<string name="title_activity_maps">Current Place Details</string>
<string name="default_info_title">Default Location</string>
<string name="default_info_snippet">No places found, because location permission is disabled.</string>
<string name="option_get_place">Get place</string>
<string name="pick_place">Choose a place</string>
In menu folder create current_place_menu.xml file and add below code:
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/option_get_place"
android:title="#string/option_get_place"
app:showAsAction="always"/>
</menu>
Hope this helps...
Makes sure you are overriding or calling OnDestroyView as well as OnDestroy. Something like ...
public override void OnDestroy()
{
base.OnDestroy();
MapView.OnDestroy();
OnDestroyView();
}
public override void OnDestroyView()
{
base.OnDestroyView();
mapReadyCallbackList.Clear();
}
Related
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);
}
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"
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);
}
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
I want to use ContentObserver for handling microphone state. But it's doesn't work. Nothing comes in method onChange.
public class MainActivity extends Activity implements OnClickListener{
AudioManager amAudioManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
amAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
getContentResolver().registerContentObserver(
System.getUriFor(System.MUTE_STREAMS_AFFECTED),
false, mMuteObserver);
}
private ContentObserver mMuteObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
amAudioManager.setMicrophoneMute(false);
}
};
#Override
public void onClick(View v) {
int id = v.getId();
//just for test functional
amAudioManager.setMicrophoneMute(true);
}
}
Android manifest file permissions:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />**