diff --git a/jami-android/app/src/main/java/cx/ring/services/CameraService.kt b/jami-android/app/src/main/java/cx/ring/services/CameraService.kt
index 33c37582fa2118323f9ab32b53d741a0ef458d49..f61adb1b1e436b4098cde43e4911635fb0a638b3 100644
--- a/jami-android/app/src/main/java/cx/ring/services/CameraService.kt
+++ b/jami-android/app/src/main/java/cx/ring/services/CameraService.kt
@@ -118,6 +118,9 @@ class CameraService internal constructor(c: Context) {
     val maxResolutions: Observable<Pair<Int?, Int?>>
         get() = maxResolutionSubject
 
+    val handler: Handler
+        get() = videoHandler
+
     class VideoDevices {
         val cameras: MutableList<String> = ArrayList()
         var currentId: String? = null
diff --git a/jami-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt b/jami-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt
index df41a47818cbbd9efe974c06cc9ab80d7ebbd602..61bcd4c9e6a918dd231416324b8ca822ddb9026a 100644
--- a/jami-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt
+++ b/jami-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt
@@ -27,6 +27,7 @@ import android.media.AudioManager.OnAudioFocusChangeListener
 import android.media.MediaRecorder
 import android.media.projection.MediaProjection
 import android.os.Build
+import android.os.Handler
 import android.telecom.CallAudioState
 import android.util.Log
 import android.util.Size
@@ -85,6 +86,8 @@ class HardwareServiceImpl(
     init {
         pushLogEnabled = sharedPreferences.getBoolean(LOGGING_ENABLED_KEY, false)
     }
+    val handler: Handler
+        get() = cameraService.handler
 
     override fun initVideo(): Completable = cameraService.init()
 
diff --git a/jami-android/app/src/main/java/cx/ring/tv/camera/CameraPreview.kt b/jami-android/app/src/main/java/cx/ring/tv/camera/CameraPreview.kt
index 36fb8d012d2fd7e5e529476323e227a149ce8ff9..23813865d3bf8ea8aa7b2d2e2b75134527723aec 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/camera/CameraPreview.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/camera/CameraPreview.kt
@@ -17,12 +17,19 @@
 package cx.ring.tv.camera
 
 import android.content.Context
+import android.graphics.SurfaceTexture
 import android.hardware.Camera
-import android.view.SurfaceView
-import android.view.SurfaceHolder
+import android.util.Log
+import android.view.TextureView
 import java.lang.Exception
 
-class CameraPreview(context: Context, private var mCamera: Camera?) : SurfaceView(context), SurfaceHolder.Callback {
+class CameraPreview(context: Context, private var mCamera: Camera?) : TextureView(context), TextureView.SurfaceTextureListener {
+    private var mSurfaceTexture: SurfaceTexture? = null
+
+    init {
+        surfaceTextureListener = this
+    }
+
     fun stop() {
         mCamera?.let { camera ->
             try {
@@ -32,37 +39,47 @@ class CameraPreview(context: Context, private var mCamera: Camera?) : SurfaceVie
             }
             camera.release()
             mCamera = null
+            mSurfaceTexture?.release()
         }
+        surfaceTextureListener = null
     }
 
-    override fun surfaceCreated(surfaceHolder: SurfaceHolder) {
-        mCamera?.let { camera ->
-            try {
-                camera.setPreviewDisplay(surfaceHolder)
-                camera.startPreview()
-            } catch (e: Exception) {
-                // left blank for now
-            }
-        }
+    override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
+        Log.w(TAG, "Surface texture available: $width x $height")
+        startPreview(surface)
+    }
+
+    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
+        Log.w(TAG, "Surface texture changed: $width x $height")
+        startPreview(surface)
     }
 
-    override fun surfaceDestroyed(surfaceHolder: SurfaceHolder) {
+    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
+        Log.w(TAG, "Surface texture destroyed")
         stop()
+        mSurfaceTexture = null
+        return true
     }
 
-    override fun surfaceChanged(surfaceHolder: SurfaceHolder, format: Int, width: Int, height: Int) {
+    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
+        // intentionally left blank
+    }
+
+    private fun startPreview(surfaceTexture: SurfaceTexture) {
         mCamera?.let { camera ->
             try {
-                camera.setPreviewDisplay(surfaceHolder)
+                if (mSurfaceTexture != surfaceTexture) {
+                    camera.setPreviewTexture(surfaceTexture)
+                    mSurfaceTexture = surfaceTexture
+                }
                 camera.startPreview()
             } catch (e: Exception) {
-                // intentionally left blank for a test
+                Log.w(TAG, "Error starting camera preview: ${e.message}")
             }
         }
     }
 
-    // Constructor that obtains context and camera
-    init {
-        holder.addCallback(this)
+    companion object {
+        private const val TAG = "CameraPreview"
     }
 }
\ No newline at end of file
diff --git a/jami-android/app/src/main/java/cx/ring/tv/camera/CustomCameraActivity.kt b/jami-android/app/src/main/java/cx/ring/tv/camera/CustomCameraActivity.kt
index 6a99cdc8d88735b7408d0b61af3bdcfa6c3a2e46..97a1e55ad6d9a488805cdaac2e2bb248e75b7b12 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/camera/CustomCameraActivity.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/camera/CustomCameraActivity.kt
@@ -34,6 +34,7 @@ import android.media.CamcorderProfile
 import android.net.Uri
 import android.os.Build
 import android.util.Log
+import android.view.Surface
 import android.view.View
 import cx.ring.databinding.CamerapickerBinding
 import cx.ring.utils.AndroidFileUtils
@@ -244,7 +245,7 @@ class CustomCameraActivity : Activity() {
     }
 
     private fun prepareRecorder() {
-        recorder!!.setPreviewDisplay(mCameraPreview!!.holder.surface)
+        recorder!!.setPreviewDisplay(Surface(mCameraPreview!!.surfaceTexture))
         try {
             recorder!!.prepare()
             recorder!!.start()
diff --git a/jami-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt
index 3d34ef0c0d5069c7c865ef8fc3abcb6296e2e0fb..ad2f6959e7f7ff4b8fb60aaaac09e605ff8919d6 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt
@@ -27,8 +27,8 @@ import cx.ring.fragments.CallFragment
 import cx.ring.tv.call.TVCallActivity
 import cx.ring.tv.contact.more.TVContactMoreActivity
 import cx.ring.tv.contact.more.TVContactMoreFragment
-import cx.ring.tv.contactrequest.TVContactRequestDetailPresenter
 import cx.ring.tv.main.BaseDetailFragment
+import cx.ring.tv.main.HomeActivity
 import cx.ring.utils.ConversationPath
 import cx.ring.views.AvatarDrawable
 import dagger.hilt.android.AndroidEntryPoint
@@ -100,6 +100,11 @@ class TVContactFragment : BaseDetailFragment<TVContactPresenter>(), TVContactVie
         }
     }
 
+    override fun onResume() {
+        super.onResume()
+        (activity as? HomeActivity)?.enableBlur(false)
+    }
+
     override fun showContact(account: Account, model: ConversationItemViewModel) {
         val context = requireContext()
         val row = DetailsOverviewRow(model)
diff --git a/jami-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt b/jami-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt
index 88b31a476b79aa9116b94daabe2c4ca5da29f92e..ec3daa47de2627b1da285026c3747f09e2fbc7e1 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/main/HomeActivity.kt
@@ -20,7 +20,8 @@ import android.Manifest
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
-import android.graphics.drawable.ColorDrawable
+import android.graphics.RenderEffect
+import android.graphics.Shader
 import android.hardware.Camera
 import android.hardware.Camera.ErrorCallback
 import android.hardware.camera2.CameraManager
@@ -37,7 +38,6 @@ import android.util.Log
 import android.util.Size
 import android.view.KeyEvent
 import android.os.Handler
-import android.os.Looper
 import android.view.View
 import android.widget.FrameLayout
 import android.widget.ImageView
@@ -64,7 +64,12 @@ import net.jami.services.DeviceRuntimeService
 import net.jami.services.HardwareService
 import java.util.Collections
 import javax.inject.Inject
+import androidx.core.view.isGone
+import androidx.core.graphics.createBitmap
+import androidx.core.graphics.drawable.toDrawable
+import androidx.core.view.isInvisible
 
+@Suppress("DEPRECATION")
 @AndroidEntryPoint
 class HomeActivity : FragmentActivity() {
     private val mDisposableBag = CompositeDisposable()
@@ -90,24 +95,42 @@ class HomeActivity : FragmentActivity() {
     private var out: Allocation? = null
     private var mBlurOut: Allocation? = null
     private var cameraPermissionIsRefusedFlag = false // to not ask for permission again if refused
+    private var paused = false
+    private var renderEffect: RenderEffect? = null
 
     private val mErrorCallback = ErrorCallback { error, camera ->
+        Log.w(TAG, "Camera error: $error")
         try {
             mBlurImage.visibility = View.INVISIBLE
             mBackgroundManager.drawable =
-                ContextCompat.getDrawable(this@HomeActivity, R.drawable.tv_background)
-                    ?: ColorDrawable(getColor(R.color.colorPrimary))
+                ContextCompat.getDrawable(this@HomeActivity, R.drawable.background_welcome_jami)
+                    ?: getColor(R.color.colorPrimary).toDrawable()
+            mCamera = null
+            mPreviewView.removeAllViews()
+            mCameraPreview?.stop()
         } catch (e: Exception) {
             Log.e(TAG, "ErrorCallback", e)
         }
     }
     private val mCameraAvailabilityCallback: AvailabilityCallback = object : AvailabilityCallback() {
         override fun onCameraAvailable(cameraId: String) {
-            if (mBlurImage.visibility == View.INVISIBLE) {
+            Log.w(TAG, "onCameraAvailable $cameraId paused:$paused")
+            if (!paused && mBlurImage.isInvisible) {
                 checkCameraAvailability()
             }
         }
     }
+
+    fun enableBlur(enable: Boolean) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            if (enable) {
+                mPreviewView.setRenderEffect(renderEffect)
+            } else {
+                mPreviewView.setRenderEffect(null)
+            }
+        }
+    }
+
     private val mPreviewCallback = Camera.PreviewCallback { data, camera ->
         if (supportFragmentManager.findFragmentByTag(TVContactFragment.TAG) != null) {
             mBlurImage.visibility = View.GONE
@@ -127,7 +150,7 @@ class HomeActivity : FragmentActivity() {
                 setRadius(BLUR_RADIUS * size.width / 1080)
                 setInput(out)
             }
-            mBlurOutputBitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888)
+            mBlurOutputBitmap = createBitmap(size.width, size.height)
             mBlurOut = Allocation.createFromBitmap(rs, mBlurOutputBitmap)
         }
         input!!.copyFrom(data)
@@ -135,7 +158,7 @@ class HomeActivity : FragmentActivity() {
         blurIntrinsic!!.forEach(mBlurOut)
         mBlurOut!!.copyTo(mBlurOutputBitmap)
         mBlurImage.setImageBitmap(mBlurOutputBitmap)
-        if (mBlurImage.visibility == View.GONE) {
+        if (mBlurImage.isGone) {
             mPreviewView.visibility = View.INVISIBLE
             mBlurImage.visibility = View.VISIBLE
             mFadeView.visibility = View.VISIBLE
@@ -152,6 +175,13 @@ class HomeActivity : FragmentActivity() {
         mPreviewView = findViewById(R.id.previewView)
         mBlurImage = findViewById(R.id.blur)
         mFadeView = findViewById(R.id.fade)
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            val radius = BLUR_RADIUS * TARGET_SIZE.width / 1080 * resources.displayMetrics.density
+            renderEffect = RenderEffect.createBlurEffect(
+                radius, radius,
+                Shader.TileMode.MIRROR
+            )
+        }
     }
 
     override fun onBackPressed() {
@@ -189,16 +219,23 @@ class HomeActivity : FragmentActivity() {
 
     override fun onPostResume() {
         super.onPostResume()
+        paused = false
         checkCameraAvailability()
     }
 
     override fun onPause() {
         super.onPause()
+        paused = true
         mCameraPreview?.let { preview ->
-            mCamera?.setPreviewCallback(null)
+            mCamera?.let { camera ->
+                camera.release();
+                mCamera = null
+            }
             preview.stop()
             mCameraPreview = null
+            mPreviewView.removeAllViews()
         }
+        mDisposableBag.clear()
     }
 
     override fun onDestroy() {
@@ -251,7 +288,7 @@ class HomeActivity : FragmentActivity() {
         val w = target.width
         val h = target.height
         for (option in choices) {
-            Log.w(TAG, "supportedSize: $option")
+            //Log.w(TAG, "supportedSize: $option")
             if (option.width <= maxWidth && option.height <= maxHeight && option.height == option.width * h / w) {
                 if (option.width >= minWidth && option.height >= minHeight) {
                     bigEnough.add(option)
@@ -264,8 +301,8 @@ class HomeActivity : FragmentActivity() {
         // Pick the smallest of those big enough. If there is no one big enough, pick the
         // largest of those not big enough.
         return when {
-            bigEnough.size > 0 -> Collections.min(bigEnough, CompareSizesByArea())
-            notBigEnough.size > 0 -> Collections.max(notBigEnough, CompareSizesByArea())
+            bigEnough.isNotEmpty() -> Collections.min(bigEnough, CompareSizesByArea())
+            notBigEnough.isNotEmpty() -> Collections.max(notBigEnough, CompareSizesByArea())
             else -> {
                 Log.e(TAG, "Couldn't find any suitable preview size")
                 choices[0]
@@ -274,39 +311,70 @@ class HomeActivity : FragmentActivity() {
     }
 
     private fun setUpCamera() {
+        Log.w(TAG, "setUpCamera()")
+        if (mCamera != null) {
+            Log.w(TAG, "setUpCamera() camera already set up")
+            return
+        }
         mDisposableBag.add(Single.fromCallable {
             val currentCamera = 0
-            Camera.open(currentCamera)!!.apply { mCamera = this }
+            if (mCameraManager == null) {
+                mCameraManager = (getSystemService(CAMERA_SERVICE) as CameraManager).apply {
+                    registerAvailabilityCallback((mCameraAvailabilityCallback), Handler(mainLooper))
+                }
+            }
+            mCameraPreview?.stop()
+
+            val camera = try {
+                Camera.open(currentCamera)
+            } catch (e: Exception) {
+                Log.e(TAG, "setUpCamera() failed to open camera", e)
+                throw RuntimeException("Camera unavailable", e)
+            }
+
+            camera.apply {
+                val params = parameters
+                Log.w(TAG, "setUpCamera() supportedPictureSizes: ${params.previewSize.width}, x: ${params.previewSize.height}")
+                val selectSize = chooseOptimalSize(params.supportedPictureSizes, 1280, 720, 1920, 1080, TARGET_SIZE)
+                Log.w(TAG, "setUpCamera() selectSize: ${selectSize.width}, x: ${selectSize.height}")
+                params.setPictureSize(selectSize.width, selectSize.height)
+                params.setPreviewSize(selectSize.width, selectSize.height)
+                setErrorCallback(mErrorCallback)
+                try {
+                    parameters = params
+                } catch (e: Exception) {
+                    Log.e(TAG, "setParameters() error", e)
+                }
+                mCamera = this
+            }
         }
             .subscribeOn(Schedulers.io())
             .observeOn(AndroidSchedulers.mainThread())
-            .subscribe({ camera: Camera ->
+            .subscribe({ camera ->
                 try {
-                    Log.w(TAG, "setUpCamera()")
-                    val params = camera.parameters
-                    val selectSize = chooseOptimalSize(params.supportedPictureSizes, 1280, 720, 1920, 1080, Size(1280, 720))
-                    Log.w(TAG, "setUpCamera() selectSize " + selectSize.width + "x" + selectSize.height)
-                    params.setPictureSize(selectSize.width, selectSize.height)
-                    params.setPreviewSize(selectSize.width, selectSize.height)
-                    camera.parameters = params
-                    mBlurImage.visibility = View.VISIBLE
+                    Log.w(TAG, "setUpCamera() $camera")
                     mFadeView.visibility = View.VISIBLE
-                    if (mCameraManager == null) {
-                        mCameraManager = (getSystemService(CAMERA_SERVICE) as CameraManager).apply {
-                            registerAvailabilityCallback((mCameraAvailabilityCallback), null)
-                        }
-                    }
-                    mCameraPreview = CameraPreview(this, camera)
+                    val cameraPreview = CameraPreview(this, camera)
                     mPreviewView.removeAllViews()
-                    mPreviewView.addView(mCameraPreview, 0)
-                    camera.setErrorCallback(mErrorCallback)
-                    camera.setPreviewCallback(mPreviewCallback)
+                    mPreviewView.addView(cameraPreview, 0)
+                    mCameraPreview = cameraPreview
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                        mBlurImage.visibility = View.INVISIBLE
+                        val radiusDp = BLUR_RADIUS * TARGET_SIZE.width / 1080
+                        val radius = radiusDp * resources.displayMetrics.density
+                        Log.w(TAG, "setUpCamera() blur radius $radius")
+                        mPreviewView.setRenderEffect(renderEffect)
+                    } else {
+                        mBlurImage.visibility = View.VISIBLE
+                        camera.setPreviewCallback(mPreviewCallback)
+                    }
                 } catch (e: Exception) {
-                    Log.e(TAG, "setUpCamera() error", e)
-                    mBackgroundManager.drawable = ContextCompat.getDrawable(this@HomeActivity, R.drawable.tv_background)
+                    Log.e(TAG, "setUpCamera() display error", e)
+                    mBackgroundManager.drawable = ContextCompat.getDrawable(this@HomeActivity, R.drawable.background_welcome_jami)
                 }
-            }) {
-                mBackgroundManager.drawable = ContextCompat.getDrawable(this@HomeActivity, R.drawable.tv_background)
+            }) { e ->
+                Log.e(TAG, "setUpCamera() error", e)
+                mBackgroundManager.drawable = ContextCompat.getDrawable(this@HomeActivity, R.drawable.background_welcome_jami)
             })
     }
 
@@ -345,6 +413,7 @@ class HomeActivity : FragmentActivity() {
 
     companion object {
         private val TAG = HomeActivity::class.simpleName!!
+        private val TARGET_SIZE = Size(1280, 720)
         private const val BLUR_RADIUS = 7.5f
     }
 }
\ No newline at end of file
diff --git a/jami-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt
index 2b9240a8756090f0c921115fd10057e861afcf7d..feb4ff5808e4b705162f3ba799592e7793f0d450 100644
--- a/jami-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt
+++ b/jami-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt
@@ -106,6 +106,13 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView {
         super.onViewCreated(view, savedInstanceState)
     }
 
+    override fun onHiddenChanged(hidden: Boolean) {
+        super.onHiddenChanged(hidden)
+        if (!hidden) {
+            (activity as? HomeActivity)?.enableBlur(true)
+        }
+    }
+
     override fun onDestroyView() {
         super.onDestroyView()
         mDisposable.clear()
diff --git a/jami-android/app/src/main/res/layout/tv_activity_home.xml b/jami-android/app/src/main/res/layout/tv_activity_home.xml
index 98b8881ca44eb9f2f9badcd7926e0793cff6e6f1..3d64a8ba6ed448a17ffa3c4089240ea2a5a93767 100644
--- a/jami-android/app/src/main/res/layout/tv_activity_home.xml
+++ b/jami-android/app/src/main/res/layout/tv_activity_home.xml
@@ -8,7 +8,7 @@
         android:id="@+id/previewView"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:visibility="invisible"/>
+        android:visibility="visible"/>
 
     <ImageView
         android:id="@+id/blur"