Creating a persistent mini player across all screens in a multi-activity Android app

I’m working on an Android app that plays videos from YouTube and other streaming services. Right now, users can keep listening to audio when they leave the main player screen. But I want to add a small player control at the bottom of every screen in the app, kind of like what Spotify and Google Music do.

The tricky part is that my app has lots of different activities and fragments. I’m not sure how to make this mini player show up everywhere. I’ve looked at a few solutions online, but they either suggest using just one main activity (which won’t work for me) or focus on the animation of the player expanding and collapsing.

Has anyone figured out how to add a persistent mini player that works across multiple activities? I’d love to hear about different approaches or libraries that might help. Here’s a basic example of what I’m trying to do:

class MiniPlayer : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.mini_player, container, false)
        // Set up player controls
        return view
    }
}

// In each activity
class SomeActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_layout)
        // How to add MiniPlayer to every activity?
    }
}

Any tips or examples would be super helpful!

Have you considered using a Service combined with a custom View? This approach can be quite effective for maintaining a persistent player across activities.

Create a Service to handle the playback logic and state. Then, design a custom View for your mini player UI. In each activity’s layout, include this custom View.

The Service can use LocalBroadcastManager to send updates, which your custom View listens for. This way, the mini player stays in sync across the app.

Here’s a basic implementation:

class PlayerService : Service() {
    private val binder = LocalBinder()
    inner class LocalBinder : Binder() {
        fun getService(): PlayerService = this@PlayerService
    }
    override fun onBind(intent: Intent): IBinder = binder
    // Implement playback logic here
}

class MiniPlayerView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
    init {
        inflate(context, R.layout.mini_player, this)
        // Set up UI and listeners
    }
}

This method keeps your activities clean while providing a consistent player experience.

I’ve dealt with this exact issue in a music streaming app I developed. One approach that worked well for me was using a custom DecorView. This method allows you to overlay the mini player on top of all your activities without modifying their individual layouts.

Here’s a rough outline of how I implemented it:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(MiniPlayerManager())
    }
}

class MiniPlayerManager : Application.ActivityLifecycleCallbacks {
    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        val rootView = activity.window.decorView as ViewGroup
        val miniPlayer = MiniPlayerView(activity)
        rootView.addView(miniPlayer)
    }
    // Implement other lifecycle methods as needed
}

This approach is quite flexible and doesn’t require you to modify your existing activity structure. The mini player gets added automatically to each activity, and you can control its visibility and behavior from the MiniPlayerView class. Just remember to handle edge cases like orientation changes and different screen sizes.

I’ve tackled a similar challenge in one of my apps. Instead of trying to add the mini player to every activity, I found it more manageable to use a custom base activity that includes the mini player layout. Then, have all your other activities extend this base activity.

Here’s a rough outline of how I approached it:

abstract class BaseActivity : AppCompatActivity() {
    override fun setContentView(layoutResID: Int) {
        val fullView = layoutInflater.inflate(R.layout.activity_base, null)
        val contentContainer = fullView.findViewById<FrameLayout>(R.id.content_container)
        layoutInflater.inflate(layoutResID, contentContainer, true)
        super.setContentView(fullView)

        supportFragmentManager.beginTransaction()
            .replace(R.id.mini_player_container, MiniPlayerFragment())
            .commit()
    }
}

class SomeActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_some)
        // Your activity-specific code
    }
}

This way, you only need to implement the mini player logic once, and it’ll be available across all activities. You might need to tweak this approach based on your specific requirements, but it should give you a solid starting point.

yo, have u tried using a bottomsheet fragment? it’s pretty cool for this kinda thing. u can add it to ur base activity and then it’ll show up everywhere. something like:

class BaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportFragmentManager.beginTransaction()
            .add(R.id.container, MiniPlayerBottomSheet())
            .commit()
    }
}

then just make all ur activities extend BaseActivity. ez pz!

hey, have u tried using a custom Application class? u can create a singleton instance of ur miniplayer there and access it from any activity. something like this:

class MyApp : Application() {
    lateinit var miniPlayer: MiniPlayer
    override fun onCreate() {
        super.onCreate()
        miniPlayer = MiniPlayer()
    }
}

the n in ur activities, just add:
(application as MyApp).miniPlayer
to the layout. hope this helps!