• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  package android.service.notification;
17  
18  import android.annotation.FlaggedApi;
19  import android.annotation.IntDef;
20  import android.annotation.NonNull;
21  import android.annotation.Nullable;
22  import android.annotation.SuppressLint;
23  import android.annotation.SystemApi;
24  import android.app.Flags;
25  import android.app.RemoteInput;
26  import android.os.Parcel;
27  import android.os.Parcelable;
28  
29  import java.lang.annotation.Retention;
30  import java.lang.annotation.RetentionPolicy;
31  
32  /**
33   * Information about how the user has interacted with a given notification.
34   * @hide
35   */
36  @SystemApi
37  public final class NotificationStats implements Parcelable {
38  
39      private boolean mSeen;
40      private boolean mExpanded;
41      private boolean mDirectReplied;
42      private boolean mSmartReplied;
43      private boolean mSnoozed;
44      private boolean mViewedSettings;
45      private boolean mInteracted;
46  
47      /** @hide */
48      @IntDef(prefix = { "DISMISSAL_SURFACE_" }, value = {
49              DISMISSAL_NOT_DISMISSED, DISMISSAL_OTHER, DISMISSAL_PEEK, DISMISSAL_AOD,
50              DISMISSAL_SHADE, DISMISSAL_BUBBLE, DISMISSAL_LOCKSCREEN
51      })
52      @Retention(RetentionPolicy.SOURCE)
53      public @interface DismissalSurface {}
54  
55  
56      private @DismissalSurface int mDismissalSurface = DISMISSAL_NOT_DISMISSED;
57  
58      /**
59       * Notification has not been dismissed yet.
60       */
61      public static final int DISMISSAL_NOT_DISMISSED = -1;
62      /**
63       * Notification has been dismissed from a {@link NotificationListenerService} or the app
64       * itself.
65       */
66      public static final int DISMISSAL_OTHER = 0;
67      /**
68       * Notification has been dismissed while peeking.
69       */
70      public static final int DISMISSAL_PEEK = 1;
71      /**
72       * Notification has been dismissed from always on display.
73       */
74      public static final int DISMISSAL_AOD = 2;
75      /**
76       * Notification has been dismissed from the notification shade.
77       */
78      public static final int DISMISSAL_SHADE = 3;
79      /**
80       * Notification has been dismissed as a bubble.
81       * @hide
82       */
83      public static final int DISMISSAL_BUBBLE = 4;
84      /**
85       * Notification has been dismissed from the lock screen.
86       * @hide
87       */
88      public static final int DISMISSAL_LOCKSCREEN = 5;
89  
90      /** @hide */
91      @IntDef(prefix = { "DISMISS_SENTIMENT_" }, value = {
92              DISMISS_SENTIMENT_UNKNOWN, DISMISS_SENTIMENT_NEGATIVE, DISMISS_SENTIMENT_NEUTRAL,
93              DISMISS_SENTIMENT_POSITIVE
94      })
95      @Retention(RetentionPolicy.SOURCE)
96      public @interface DismissalSentiment {}
97  
98      /**
99       * No information is available about why this notification was dismissed, or the notification
100       * isn't dismissed yet.
101       */
102      public static final int DISMISS_SENTIMENT_UNKNOWN = -1000;
103      /**
104       * The user indicated while dismissing that they did not like the notification.
105       */
106      public static final int DISMISS_SENTIMENT_NEGATIVE = 0;
107      /**
108       * The user didn't indicate one way or another how they felt about the notification while
109       * dismissing it.
110       */
111      public static final int DISMISS_SENTIMENT_NEUTRAL = 1;
112      /**
113       * The user indicated while dismissing that they did like the notification.
114       */
115      public static final int DISMISS_SENTIMENT_POSITIVE = 2;
116  
117  
118      private @DismissalSentiment
119      int mDismissalSentiment = DISMISS_SENTIMENT_UNKNOWN;
120  
NotificationStats()121      public NotificationStats() {
122      }
123  
124      /**
125       * @hide
126       */
127      @SystemApi
NotificationStats(Parcel in)128      protected NotificationStats(Parcel in) {
129          mSeen = in.readByte() != 0;
130          mExpanded = in.readByte() != 0;
131          mDirectReplied = in.readByte() != 0;
132          if (Flags.lifetimeExtensionRefactor()) {
133              mSmartReplied = in.readByte() != 0;
134          }
135          mSnoozed = in.readByte() != 0;
136          mViewedSettings = in.readByte() != 0;
137          mInteracted = in.readByte() != 0;
138          mDismissalSurface = in.readInt();
139          mDismissalSentiment = in.readInt();
140      }
141  
142      @Override
writeToParcel(Parcel dest, int flags)143      public void writeToParcel(Parcel dest, int flags) {
144          dest.writeByte((byte) (mSeen ? 1 : 0));
145          dest.writeByte((byte) (mExpanded ? 1 : 0));
146          dest.writeByte((byte) (mDirectReplied ? 1 : 0));
147          if (Flags.lifetimeExtensionRefactor()) {
148              dest.writeByte((byte) (mSmartReplied ? 1 : 0));
149          }
150          dest.writeByte((byte) (mSnoozed ? 1 : 0));
151          dest.writeByte((byte) (mViewedSettings ? 1 : 0));
152          dest.writeByte((byte) (mInteracted ? 1 : 0));
153          dest.writeInt(mDismissalSurface);
154          dest.writeInt(mDismissalSentiment);
155      }
156  
157      @Override
describeContents()158      public int describeContents() {
159          return 0;
160      }
161  
162      public static final @android.annotation.NonNull Creator<NotificationStats> CREATOR = new Creator<NotificationStats>() {
163          @Override
164          public NotificationStats createFromParcel(Parcel in) {
165              return new NotificationStats(in);
166          }
167  
168          @Override
169          public NotificationStats[] newArray(int size) {
170              return new NotificationStats[size];
171          }
172      };
173  
174      /**
175       * Returns whether the user has seen this notification at least once.
176       */
hasSeen()177      public boolean hasSeen() {
178          return mSeen;
179      }
180  
181      /**
182       * Records that the user as seen this notification at least once.
183       */
setSeen()184      public void setSeen() {
185          mSeen = true;
186      }
187  
188      /**
189       * Returns whether the user has expanded this notification at least once.
190       */
hasExpanded()191      public boolean hasExpanded() {
192          return mExpanded;
193      }
194  
195      /**
196       * Records that the user has expanded this notification at least once.
197       */
setExpanded()198      public void setExpanded() {
199          mExpanded = true;
200          mInteracted = true;
201      }
202  
203      /**
204       * Returns whether the user has replied to a notification that has a
205       * {@link android.app.Notification.Action.Builder#addRemoteInput(RemoteInput) direct reply} at
206       * least once.
207       */
hasDirectReplied()208      public boolean hasDirectReplied() {
209          return mDirectReplied;
210      }
211  
212      /**
213       * Records that the user has replied to a notification that has a
214       * {@link android.app.Notification.Action.Builder#addRemoteInput(RemoteInput) direct reply}
215       * at least once.
216       */
setDirectReplied()217      public void setDirectReplied() {
218          mDirectReplied = true;
219          mInteracted = true;
220      }
221  
222      /**
223       * Returns whether the user has replied to a notification that has a smart reply at least once.
224       */
225      @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
hasSmartReplied()226      public boolean hasSmartReplied() {
227          return mSmartReplied;
228      }
229  
230      /**
231       * Records that the user has replied to a notification that has a smart reply at least once.
232       */
233      @SuppressLint("GetterSetterNames")
234      @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
setSmartReplied()235      public void setSmartReplied() {
236          mSmartReplied = true;
237          mInteracted = true;
238      }
239  
240      /**
241       * Returns whether the user has snoozed this notification at least once.
242       */
hasSnoozed()243      public boolean hasSnoozed() {
244          return mSnoozed;
245      }
246  
247      /**
248       * Records that the user has snoozed this notification at least once.
249       */
setSnoozed()250      public void setSnoozed() {
251          mSnoozed = true;
252          mInteracted = true;
253      }
254  
255      /**
256       * Returns whether the user has viewed the in-shade settings for this notification at least
257       * once.
258       */
hasViewedSettings()259      public boolean hasViewedSettings() {
260          return mViewedSettings;
261      }
262  
263      /**
264       * Records that the user has viewed the in-shade settings for this notification at least once.
265       */
setViewedSettings()266      public void setViewedSettings() {
267          mViewedSettings = true;
268          mInteracted = true;
269      }
270  
271      /**
272       * Returns whether the user has interacted with this notification beyond having viewed it.
273       */
hasInteracted()274      public boolean hasInteracted() {
275          return mInteracted;
276      }
277  
278      /**
279       * Returns from which surface the notification was dismissed.
280       */
getDismissalSurface()281      public @DismissalSurface int getDismissalSurface() {
282          return mDismissalSurface;
283      }
284  
285      /**
286       * Returns from which surface the notification was dismissed.
287       */
setDismissalSurface(@ismissalSurface int dismissalSurface)288      public void setDismissalSurface(@DismissalSurface int dismissalSurface) {
289          mDismissalSurface = dismissalSurface;
290      }
291  
292      /**
293       * Records whether the user indicated how they felt about a notification before or
294       * during dismissal.
295       */
setDismissalSentiment(@ismissalSentiment int dismissalSentiment)296      public void setDismissalSentiment(@DismissalSentiment int dismissalSentiment) {
297          mDismissalSentiment = dismissalSentiment;
298      }
299  
300      /**
301       * Returns how the user indicated they felt about a notification before or during dismissal.
302       */
getDismissalSentiment()303      public @DismissalSentiment int getDismissalSentiment() {
304          return mDismissalSentiment;
305      }
306  
307      @Override
equals(@ullable Object o)308      public boolean equals(@Nullable Object o) {
309          if (this == o) return true;
310          if (o == null || getClass() != o.getClass()) return false;
311  
312          NotificationStats that = (NotificationStats) o;
313  
314          if (mSeen != that.mSeen) return false;
315          if (mExpanded != that.mExpanded) return false;
316          if (mDirectReplied != that.mDirectReplied) return false;
317          if (Flags.lifetimeExtensionRefactor()) {
318              if (mSmartReplied != that.mSmartReplied) return false;
319          }
320          if (mSnoozed != that.mSnoozed) return false;
321          if (mViewedSettings != that.mViewedSettings) return false;
322          if (mInteracted != that.mInteracted) return false;
323          return mDismissalSurface == that.mDismissalSurface;
324      }
325  
326      @Override
hashCode()327      public int hashCode() {
328          int result = (mSeen ? 1 : 0);
329          result = 31 * result + (mExpanded ? 1 : 0);
330          result = 31 * result + (mDirectReplied ? 1 : 0);
331          if (Flags.lifetimeExtensionRefactor()) {
332              result = 31 * result + (mSmartReplied ? 1 : 0);
333          }
334          result = 31 * result + (mSnoozed ? 1 : 0);
335          result = 31 * result + (mViewedSettings ? 1 : 0);
336          result = 31 * result + (mInteracted ? 1 : 0);
337          result = 31 * result + mDismissalSurface;
338          return result;
339      }
340  
341      @NonNull
342      @Override
toString()343      public String toString() {
344          final StringBuilder sb = new StringBuilder("NotificationStats{");
345          sb.append("mSeen=").append(mSeen);
346          sb.append(", mExpanded=").append(mExpanded);
347          sb.append(", mDirectReplied=").append(mDirectReplied);
348          if (Flags.lifetimeExtensionRefactor()) {
349              sb.append(", mSmartReplied=").append(mSmartReplied);
350          }
351          sb.append(", mSnoozed=").append(mSnoozed);
352          sb.append(", mViewedSettings=").append(mViewedSettings);
353          sb.append(", mInteracted=").append(mInteracted);
354          sb.append(", mDismissalSurface=").append(mDismissalSurface);
355          sb.append('}');
356          return sb.toString();
357      }
358  }
359