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