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

Commit ce706dfd authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use @NotifInflation thread for ExpandableNotificationRow" into main

parents f6a70c95 f73e2ac7
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -983,6 +983,16 @@ flag {
    }
}

flag {
    name: "use_notif_inflation_thread_for_row"
    namespace: "systemui"
    description: "use the @NotifInflation thread for ExpandableNotificationRow inflation"
    bug: "375320642"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "notify_power_manager_user_activity_background"
    namespace: "systemui"
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.statusbar.notification.row

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.annotation.UiThread
import com.android.app.tracing.coroutines.launchTraced
import com.android.app.tracing.coroutines.withContextTraced
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.NotifInflation
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job

@SysUISingleton
class AsyncRowInflater
@Inject
constructor(
    @Application private val applicationScope: CoroutineScope,
    @Main private val mainCoroutineDispatcher: CoroutineDispatcher,
    @NotifInflation private val inflationCoroutineDispatcher: CoroutineDispatcher,
) {
    /**
     * Inflate the layout on the background thread, and invoke the listener on the main thread when
     * finished.
     *
     * If the inflation fails on the background, it will be retried once on the main thread.
     */
    @UiThread
    fun inflate(
        context: Context,
        layoutFactory: LayoutInflater.Factory2,
        @LayoutRes resId: Int,
        parent: ViewGroup,
        listener: OnInflateFinishedListener,
    ): Job {
        val inflater = BasicRowInflater(context).apply { factory2 = layoutFactory }
        return applicationScope.launchTraced("AsyncRowInflater-bg", inflationCoroutineDispatcher) {
            val view =
                try {
                    inflater.inflate(resId, parent, false)
                } catch (ex: RuntimeException) {
                    // Probably a Looper failure, retry on the UI thread
                    Log.w(
                        "AsyncRowInflater",
                        "Failed to inflate resource in the background!" +
                            " Retrying on the UI thread",
                        ex,
                    )
                    null
                }
            withContextTraced("AsyncRowInflater-ui", mainCoroutineDispatcher) {
                // If the inflate failed on the inflation thread, try again on the main thread
                val finalView = view ?: inflater.inflate(resId, parent, false)
                // Inform the listener of the completion
                listener.onInflateFinished(finalView, resId, parent)
            }
        }
    }

    /**
     * Callback interface (identical to the one from AsyncLayoutInflater) for receiving the inflated
     * view
     */
    interface OnInflateFinishedListener {
        @UiThread fun onInflateFinished(view: View, @LayoutRes resId: Int, parent: ViewGroup?)
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.statusbar.notification.row

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View

/**
 * A [LayoutInflater] that is copy of
 * [androidx.asynclayoutinflater.view.AsyncLayoutInflater.BasicInflater]
 */
internal class BasicRowInflater(context: Context) : LayoutInflater(context) {
    override fun cloneInContext(newContext: Context): LayoutInflater {
        return BasicRowInflater(newContext)
    }

    @Throws(ClassNotFoundException::class)
    override fun onCreateView(name: String, attrs: AttributeSet): View {
        for (prefix in sClassPrefixList) {
            try {
                val view = createView(name, prefix, attrs)
                if (view != null) {
                    return view
                }
            } catch (e: ClassNotFoundException) {
                // In this case we want to let the base class take a crack at it.
            }
        }

        return super.onCreateView(name, attrs)
    }

    companion object {
        private val sClassPrefixList = arrayOf("android.widget.", "android.webkit.", "android.app.")
    }
}
+14 −37
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.asynclayoutinflater.view.AsyncLayoutFactory;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;

import com.android.systemui.Flags;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.InflationTask;
@@ -44,7 +45,8 @@ import javax.inject.Inject;
/**
 * An inflater task that asynchronously inflates a ExpandableNotificationRow
 */
public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
public class RowInflaterTask implements InflationTask,
        AsyncLayoutInflater.OnInflateFinishedListener, AsyncRowInflater.OnInflateFinishedListener {

    private static final String TAG = "RowInflaterTask";
    private static final boolean TRACE_ORIGIN = true;
@@ -55,15 +57,17 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
    private Throwable mInflateOrigin;
    private final SystemClock mSystemClock;
    private final RowInflaterTaskLogger mLogger;
    private final AsyncRowInflater mAsyncRowInflater;
    private long mInflateStartTimeMs;
    private UserTracker mUserTracker;

    @Inject
    public RowInflaterTask(SystemClock systemClock, RowInflaterTaskLogger logger,
            UserTracker userTracker) {
            UserTracker userTracker, AsyncRowInflater asyncRowInflater) {
        mSystemClock = systemClock;
        mLogger = logger;
        mUserTracker = userTracker;
        mAsyncRowInflater = asyncRowInflater;
    }

    /**
@@ -87,14 +91,20 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
            mInflateOrigin = new Throwable("inflate requested here");
        }
        mListener = listener;
        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, makeRowInflater(entry));
        RowAsyncLayoutInflater asyncLayoutFactory = makeRowInflater(entry);
        mEntry = entry;
        entry.setInflationTask(this);

        mLogger.logInflateStart(entry);
        mInflateStartTimeMs = mSystemClock.elapsedRealtime();
        if (Flags.useNotifInflationThreadForRow()) {
            mAsyncRowInflater.inflate(context, asyncLayoutFactory,
                    R.layout.status_bar_notification_row, parent, this);
        } else {
            AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, asyncLayoutFactory);
            inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this);
        }
    }

    /**
     * Inflates a new notificationView synchronously.
@@ -117,39 +127,6 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
                entry, mSystemClock, mLogger, mUserTracker.getUserHandle());
    }

    /**
     * A {@link LayoutInflater} that is copy of BasicLayoutInflater.
     */
    private static class BasicRowInflater extends LayoutInflater {
        private static final String[] sClassPrefixList =
                {"android.widget.", "android.webkit.", "android.app."};
        BasicRowInflater(Context context) {
            super(context);
        }

        @Override
        public LayoutInflater cloneInContext(Context newContext) {
            return new BasicRowInflater(newContext);
        }

        @Override
        protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
            for (String prefix : sClassPrefixList) {
                try {
                    View view = createView(name, prefix, attrs);
                    if (view != null) {
                        return view;
                    }
                } catch (ClassNotFoundException e) {
                    // In this case we want to let the base class take a crack
                    // at it.
                }
            }

            return super.onCreateView(name, attrs);
        }
    }

    @VisibleForTesting
    public static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
        private final NotificationEntry mEntry;
+2 −1
Original line number Diff line number Diff line
@@ -354,7 +354,8 @@ class ExpandableNotificationRowBuilder(
            RowInflaterTask(
                mFakeSystemClock,
                Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY),
                userTracker
                userTracker,
                Mockito.mock(AsyncRowInflater::class.java, STUB_ONLY),
            )
        val row = rowInflaterTask.inflateSynchronously(context, null, entry)