1 /*
2  * Copyright 2024 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.server.appsearch.external.localstorage.stats;
18 
19 import android.annotation.NonNull;
20 import android.app.appsearch.annotation.CanIgnoreReturnValue;
21 
22 import java.util.Objects;
23 
24 // TODO(b/319285816): link converter here.
25 /**
26  * Class holds detailed stats of a click action, converted from {@link
27  * android.app.appsearch.PutDocumentsRequest#getTakenActionGenericDocuments}.
28  *
29  * @hide
30  */
31 public class ClickStats {
32     private final long mTimestampMillis;
33 
34     private final long mTimeStayOnResultMillis;
35 
36     private final int mResultRankInBlock;
37 
38     private final int mResultRankGlobal;
39 
40     private final boolean mIsGoodClick;
41 
ClickStats(@onNull Builder builder)42     ClickStats(@NonNull Builder builder) {
43         Objects.requireNonNull(builder);
44         mTimestampMillis = builder.mTimestampMillis;
45         mTimeStayOnResultMillis = builder.mTimeStayOnResultMillis;
46         mResultRankInBlock = builder.mResultRankInBlock;
47         mResultRankGlobal = builder.mResultRankGlobal;
48         mIsGoodClick = builder.mIsGoodClick;
49     }
50 
51     /** Returns the click action timestamp in milliseconds since Unix epoch. */
getTimestampMillis()52     public long getTimestampMillis() {
53         return mTimestampMillis;
54     }
55 
56     /** Returns the time (duration) of the user staying on the clicked result. */
getTimeStayOnResultMillis()57     public long getTimeStayOnResultMillis() {
58         return mTimeStayOnResultMillis;
59     }
60 
61     /** Returns the in-block rank of the clicked result. */
getResultRankInBlock()62     public int getResultRankInBlock() {
63         return mResultRankInBlock;
64     }
65 
66     /** Returns the global rank of the clicked result. */
getResultRankGlobal()67     public int getResultRankGlobal() {
68         return mResultRankGlobal;
69     }
70 
71     /**
72      * Returns whether this click is a good click or not.
73      *
74      * @see Builder#setIsGoodClick
75      */
isGoodClick()76     public boolean isGoodClick() {
77         return mIsGoodClick;
78     }
79 
80     /** Builder for {@link ClickStats} */
81     public static final class Builder {
82         private long mTimestampMillis;
83 
84         private long mTimeStayOnResultMillis;
85 
86         private int mResultRankInBlock;
87 
88         private int mResultRankGlobal;
89 
90         private boolean mIsGoodClick = true;
91 
92         /** Sets the click action timestamp in milliseconds since Unix epoch. */
93         @CanIgnoreReturnValue
94         @NonNull
setTimestampMillis(long timestampMillis)95         public Builder setTimestampMillis(long timestampMillis) {
96             mTimestampMillis = timestampMillis;
97             return this;
98         }
99 
100         /** Sets the time (duration) of the user staying on the clicked result. */
101         @CanIgnoreReturnValue
102         @NonNull
setTimeStayOnResultMillis(long timeStayOnResultMillis)103         public Builder setTimeStayOnResultMillis(long timeStayOnResultMillis) {
104             mTimeStayOnResultMillis = timeStayOnResultMillis;
105             return this;
106         }
107 
108         /** Sets the in-block rank of the clicked result. */
109         @CanIgnoreReturnValue
110         @NonNull
setResultRankInBlock(int resultRankInBlock)111         public Builder setResultRankInBlock(int resultRankInBlock) {
112             mResultRankInBlock = resultRankInBlock;
113             return this;
114         }
115 
116         /** Sets the global rank of the clicked result. */
117         @CanIgnoreReturnValue
118         @NonNull
setResultRankGlobal(int resultRankGlobal)119         public Builder setResultRankGlobal(int resultRankGlobal) {
120             mResultRankGlobal = resultRankGlobal;
121             return this;
122         }
123 
124         /**
125          * Sets the flag indicating whether the click is good or not.
126          *
127          * <p>A good click means the user is satisfied by the clicked document. The caller should
128          * define its own criteria and set this field accordingly.
129          *
130          * <p>The default value is true if unset. We should treat it as a good click by default if
131          * the caller didn't specify or could not determine for several reasons:
132          *
133          * <ul>
134          *   <li>It may be difficult for the caller to determine if the user is satisfied by the
135          *       clicked document or not.
136          *   <li>AppSearch collects search quality metrics that are related to number of good
137          *       clicks. We don't want to demote the quality score aggressively by the undetermined
138          *       ones.
139          * </ul>
140          */
141         @CanIgnoreReturnValue
142         @NonNull
setIsGoodClick(boolean isGoodClick)143         public Builder setIsGoodClick(boolean isGoodClick) {
144             mIsGoodClick = isGoodClick;
145             return this;
146         }
147 
148         /** Builds a new {@link ClickStats} from the {@link ClickStats.Builder}. */
149         @NonNull
build()150         public ClickStats build() {
151             return new ClickStats(/* builder= */ this);
152         }
153     }
154 }
155