I've created a simple app with the bottom navigation bar and few fragments where one fragment (ArFragment) shows a camera preview:
MainActivity.kt:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bottomBar = findViewById<BottomNavigationView>(R.id.bottom_navigation)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
bottomBar.setupWithNavController(navHostFragment.navController)
}
}
activity_main.xml:
<LinearLayout
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:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:menu="@menu/bottom_navigation_menu" />
</LinearLayout>
ArFragment.kt:
class ArFragment : Fragment() {
private lateinit var cameraExecutor: ExecutorService
private lateinit var binding: FragmentArBinding
private val cameraPermissionResultReceiver =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
startCamera()
} else {
binding.viewFinder.visibility = View.GONE
binding.errorTextView.visibility = View.VISIBLE
binding.frameLayout.invalidate()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentArBinding.inflate(inflater, container, false)
// Request camera permissions
when {
allPermissionsGranted() -> startCamera()
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
showRationaleDialog()
}
else -> cameraPermissionResultReceiver.launch(Manifest.permission.CAMERA)
}
cameraExecutor = Executors.newSingleThreadExecutor()
return binding.root
}
private fun showRationaleDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.rationale_title_camera)
.setMessage(R.string.rationale_body_camera)
.setPositiveButton(R.string.action_ok) { _, _ ->
cameraPermissionResultReceiver.launch(Manifest.permission.CAMERA)
}
.create()
.show()
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
private fun startCamera() {
context?.let { ctx ->
val cameraProviderFuture = ProcessCameraProvider.getInstance(ctx)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(binding.viewFinder.surfaceProvider)
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview
)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(ctx))
}
}
private fun allPermissionsGranted() = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
companion object {
private const val TAG = "ArFragment"
}
}
fragment_ar.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.ArFragment">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/errorTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_margin="24dp"
android:visibility="gone"
android:text="@string/error_no_camera_permissions" />
</FrameLayout>
The camera preview works perfectly but I can't make it to fit all screen space, please, don't confuse with a fullscreen mode. I want to make status, tab and navigation bars transparent for ArFragment (and only for ArFragment). In short, to the camera preview became like a root layer background. A something similar to our iOS version:
P.S. I've tried solutions from similar questions. To use android:fitsSystemWindows="true" and so on. It works, it makes them transparent but I need to the camera preview could fill all available screen space. At this moment I think that the camera output must be rendered as a root layout background but I am not sure if it's a right way. And even if it's a right way, I don't know how to implement it.

