1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.notification.row; 18 19 import android.content.Context; 20 import android.util.AttributeSet; 21 import android.util.Log; 22 import android.view.View; 23 import android.view.ViewGroup; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 import androidx.annotation.VisibleForTesting; 28 import androidx.asynclayoutinflater.view.AsyncLayoutFactory; 29 import androidx.asynclayoutinflater.view.AsyncLayoutInflater; 30 31 import com.android.systemui.res.R; 32 import com.android.systemui.statusbar.InflationTask; 33 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 34 import com.android.systemui.util.time.SystemClock; 35 36 import java.util.concurrent.Executor; 37 38 import javax.inject.Inject; 39 40 /** 41 * An inflater task that asynchronously inflates a ExpandableNotificationRow 42 */ 43 public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener { 44 45 private static final String TAG = "RowInflaterTask"; 46 private static final boolean TRACE_ORIGIN = true; 47 48 private RowInflationFinishedListener mListener; 49 private NotificationEntry mEntry; 50 private boolean mCancelled; 51 private Throwable mInflateOrigin; 52 private final SystemClock mSystemClock; 53 private final RowInflaterTaskLogger mLogger; 54 private long mInflateStartTimeMs; 55 56 @Inject RowInflaterTask(SystemClock systemClock, RowInflaterTaskLogger logger)57 public RowInflaterTask(SystemClock systemClock, RowInflaterTaskLogger logger) { 58 mSystemClock = systemClock; 59 mLogger = logger; 60 } 61 62 /** 63 * Inflates a new notificationView asynchronously, calling the {@code listener} on the main 64 * thread when done. This should not be called twice on this object. 65 */ inflate(Context context, ViewGroup parent, NotificationEntry entry, RowInflationFinishedListener listener)66 public void inflate(Context context, ViewGroup parent, NotificationEntry entry, 67 RowInflationFinishedListener listener) { 68 inflate(context, parent, entry, null, listener); 69 } 70 71 /** 72 * Inflates a new notificationView asynchronously, calling the {@code listener} on the supplied 73 * {@code listenerExecutor} (or the main thread if null) when done. This should not be called 74 * twice on this object. 75 */ 76 @VisibleForTesting inflate(Context context, ViewGroup parent, NotificationEntry entry, @Nullable Executor listenerExecutor, RowInflationFinishedListener listener)77 public void inflate(Context context, ViewGroup parent, NotificationEntry entry, 78 @Nullable Executor listenerExecutor, RowInflationFinishedListener listener) { 79 if (TRACE_ORIGIN) { 80 mInflateOrigin = new Throwable("inflate requested here"); 81 } 82 mListener = listener; 83 AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, makeRowInflater(entry)); 84 mEntry = entry; 85 entry.setInflationTask(this); 86 87 mLogger.logInflateStart(entry); 88 mInflateStartTimeMs = mSystemClock.elapsedRealtime(); 89 inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this); 90 } 91 makeRowInflater(NotificationEntry entry)92 private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) { 93 return new RowAsyncLayoutInflater(entry, mSystemClock, mLogger); 94 } 95 96 @VisibleForTesting 97 public static class RowAsyncLayoutInflater implements AsyncLayoutFactory { 98 private final NotificationEntry mEntry; 99 private final SystemClock mSystemClock; 100 private final RowInflaterTaskLogger mLogger; 101 RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock, RowInflaterTaskLogger logger)102 public RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock, 103 RowInflaterTaskLogger logger) { 104 mEntry = entry; 105 mSystemClock = systemClock; 106 mLogger = logger; 107 } 108 109 @Nullable 110 @Override onCreateView(@ullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs)111 public View onCreateView(@Nullable View parent, @NonNull String name, 112 @NonNull Context context, @NonNull AttributeSet attrs) { 113 if (!name.equals(ExpandableNotificationRow.class.getName())) { 114 return null; 115 } 116 117 final long startMs = mSystemClock.elapsedRealtime(); 118 final ExpandableNotificationRow row = 119 new ExpandableNotificationRow(context, attrs, mEntry); 120 final long elapsedMs = mSystemClock.elapsedRealtime() - startMs; 121 122 mLogger.logCreatedRow(mEntry, elapsedMs); 123 124 return row; 125 } 126 127 @Nullable 128 @Override onCreateView(@onNull String name, @NonNull Context context, @NonNull AttributeSet attrs)129 public View onCreateView(@NonNull String name, @NonNull Context context, 130 @NonNull AttributeSet attrs) { 131 return null; 132 } 133 } 134 135 @Override abort()136 public void abort() { 137 mCancelled = true; 138 } 139 140 @Override onInflateFinished(View view, int resid, ViewGroup parent)141 public void onInflateFinished(View view, int resid, ViewGroup parent) { 142 final long elapsedMs = mSystemClock.elapsedRealtime() - mInflateStartTimeMs; 143 mLogger.logInflateFinish(mEntry, elapsedMs, mCancelled); 144 145 if (!mCancelled) { 146 try { 147 mEntry.onInflationTaskFinished(); 148 mListener.onInflationFinished((ExpandableNotificationRow) view); 149 } catch (Throwable t) { 150 if (mInflateOrigin != null) { 151 Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin); 152 t.addSuppressed(mInflateOrigin); 153 } 154 throw t; 155 } 156 } 157 } 158 159 public interface RowInflationFinishedListener { onInflationFinished(ExpandableNotificationRow row)160 void onInflationFinished(ExpandableNotificationRow row); 161 } 162 } 163