Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05c7ed41 authored by Anton Potapov's avatar Anton Potapov
Browse files

Add new Volume Dialog dagger and coroutines infra

This change adds two new dagger subcomponents alongside the scoped
CoroutineScopes:
1) Plugin component which lives alongside the plugin initialization.
   This effectively is @Application scope now, because it will almost
never be destroyed. Having it separate will make the possible future work to attempl to move Volume Dialog to its own scope easier
2) Dialog component which lives with the Volume Dialog. This one is
   destroyed alongside the dialog itself and is more conservative in
terms of keeping stuff on a global scope. All UI work should happen here

Flag: com.android.systemui.volume_redesign
Test: passes presubmits
Bug: 369994090
Change-Id: Idab663549559c28c3954864ebc5c4ab50954d2d8
parent efc0c5a3
Loading
Loading
Loading
Loading
+34 −25
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.Looper;

import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.CoreStartable;
import com.android.systemui.Flags;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dialog.MediaOutputDialogManager;
import com.android.systemui.plugins.VolumeDialog;
@@ -40,6 +41,8 @@ import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumePanelDialogReceiver;
import com.android.systemui.volume.VolumeUI;
import com.android.systemui.volume.dialog.VolumeDialogPlugin;
import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent;
import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
@@ -66,7 +69,8 @@ import dagger.multibindings.IntoSet;
                SpatializerModule.class,
        },
        subcomponents = {
                VolumePanelComponent.class
                VolumePanelComponent.class,
                VolumeDialogPluginComponent.class,
        }
)
public interface VolumeModule {
@@ -101,6 +105,7 @@ public interface VolumeModule {
    /**  */
    @Provides
    static VolumeDialog provideVolumeDialog(
            Lazy<VolumeDialogPlugin> newVolumeDialogProvider,
            Context context,
            VolumeDialogController volumeDialogController,
            AccessibilityManagerWrapper accessibilityManagerWrapper,
@@ -118,6 +123,9 @@ public interface VolumeModule {
            VibratorHelper vibratorHelper,
            SystemClock systemClock,
            VolumeDialogInteractor interactor) {
        if (Flags.volumeRedesign()) {
            return newVolumeDialogProvider.get();
        } else {
            VolumeDialogImpl impl = new VolumeDialogImpl(
                    context,
                    volumeDialogController,
@@ -144,3 +152,4 @@ public interface VolumeModule {
            return impl;
        }
    }
}
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.volume.dialog

import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.ContextThemeWrapper
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import javax.inject.Inject

class VolumeDialog @Inject constructor(@Application context: Context) :
    Dialog(ContextThemeWrapper(context, R.style.volume_dialog_theme)) {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.volume_dialog)
    }
}
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.volume.dialog

import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.VolumeDialog
import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

class VolumeDialogPlugin
@Inject
constructor(
    @Application private val applicationCoroutineScope: CoroutineScope,
    private val volumeDialogPluginComponentFactory: VolumeDialogPluginComponent.Factory,
) : VolumeDialog {

    private var volumeDialogPluginComponent: VolumeDialogPluginComponent? = null
    private var job: Job? = null

    override fun init(windowType: Int, callback: VolumeDialog.Callback?) {
        job =
            applicationCoroutineScope.launch {
                coroutineScope {
                    volumeDialogPluginComponent = volumeDialogPluginComponentFactory.create(this)
                }
            }
    }

    private fun showDialog() {
        val volumeDialogPluginComponent =
            volumeDialogPluginComponent ?: error("Creating dialog before init was called")
        volumeDialogPluginComponent.coroutineScope().launch {
            coroutineScope {
                val volumeDialogComponent: VolumeDialogComponent =
                    volumeDialogPluginComponent.volumeDialogComponentFactory().create(this)
                with(volumeDialogComponent.volumeDialog()) {
                    setOnDismissListener { volumeDialogComponent.coroutineScope().cancel() }
                    show()
                }
            }
        }
    }

    override fun destroy() {
        job?.cancel()
    }
}
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.volume.dialog.dagger

import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import dagger.BindsInstance
import dagger.Subcomponent
import kotlinx.coroutines.CoroutineScope

/**
 * Core Volume Dialog dagger component. It's managed by
 * [com.android.systemui.volume.dialog.VolumeDialogPlugin] and lives alongside it.
 */
@VolumeDialogScope
@Subcomponent(modules = [])
interface VolumeDialogComponent {

    /**
     * Provides a coroutine scope to use inside [VolumeDialogScope].
     * [com.android.systemui.volume.dialog.VolumeDialogPlugin] manages the lifecycle of this scope.
     * It's cancelled when the dialog is disposed. This helps to free occupied resources when volume
     * dialog is not shown.
     */
    @VolumeDialog fun coroutineScope(): CoroutineScope

    @VolumeDialogScope fun volumeDialog(): com.android.systemui.volume.dialog.VolumeDialog

    @Subcomponent.Factory
    interface Factory {

        fun create(@BindsInstance @VolumeDialog scope: CoroutineScope): VolumeDialogComponent
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.volume.dialog.dagger

import com.android.systemui.volume.dialog.dagger.module.VolumeDialogPluginModule
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
import dagger.BindsInstance
import dagger.Subcomponent
import kotlinx.coroutines.CoroutineScope

/**
 * Volume Dialog plugin dagger component. It's managed by
 * [com.android.systemui.volume.dialog.VolumeDialogPlugin] and lives alongside it.
 */
@VolumeDialogPluginScope
@Subcomponent(modules = [VolumeDialogPluginModule::class])
interface VolumeDialogPluginComponent {

    /**
     * Provides a coroutine scope to use inside [VolumeDialogPluginScope].
     * [com.android.systemui.volume.dialog.VolumeDialogPlugin] manages the lifecycle of this scope.
     * It's cancelled when the dialog is disposed. This helps to free occupied resources when volume
     * dialog is not shown.
     */
    @VolumeDialogPlugin fun coroutineScope(): CoroutineScope

    fun volumeDialogComponentFactory(): VolumeDialogComponent.Factory

    @Subcomponent.Factory
    interface Factory {

        fun create(
            @BindsInstance @VolumeDialogPlugin scope: CoroutineScope
        ): VolumeDialogPluginComponent
    }
}
Loading