ChannelImpressions.java revision 0350dab5b690652f78024a211c993f32fb766876
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
17package android.ext.services.notification;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.text.TextUtils;
22import android.util.Log;
23
24import com.android.internal.annotations.VisibleForTesting;
25
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlSerializer;
28
29import java.io.IOException;
30
31public final class ChannelImpressions implements Parcelable {
32    private static final String TAG = "ExtAssistant.CI";
33    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
34
35    static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .4f;
36    static final int DEFAULT_STREAK_LIMIT = 2;
37    static final String ATT_DISMISSALS = "dismisses";
38    static final String ATT_VIEWS = "views";
39    static final String ATT_STREAK = "streak";
40
41    private int mDismissals = 0;
42    private int mViews = 0;
43    private int mStreak = 0;
44
45    private float mDismissToViewRatioLimit;
46    private int mStreakLimit;
47
48    public ChannelImpressions() {
49        mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT;
50        mStreakLimit = DEFAULT_STREAK_LIMIT;
51    }
52
53    protected ChannelImpressions(Parcel in) {
54        mDismissals = in.readInt();
55        mViews = in.readInt();
56        mStreak = in.readInt();
57        mDismissToViewRatioLimit = in.readFloat();
58        mStreakLimit = in.readInt();
59    }
60
61    public int getStreak() {
62        return mStreak;
63    }
64
65    public int getDismissals() {
66        return mDismissals;
67    }
68
69    public int getViews() {
70        return mViews;
71    }
72
73    public void incrementDismissals() {
74        mDismissals++;
75        mStreak++;
76    }
77
78    void updateThresholds(float dismissToViewRatioLimit, int streakLimit) {
79        mDismissToViewRatioLimit = dismissToViewRatioLimit;
80        mStreakLimit = streakLimit;
81    }
82
83    @VisibleForTesting
84    float getDismissToViewRatioLimit() {
85        return mDismissToViewRatioLimit;
86    }
87
88    @VisibleForTesting
89    int getStreakLimit() {
90        return mStreakLimit;
91    }
92
93    public void append(ChannelImpressions additionalImpressions) {
94        if (additionalImpressions != null) {
95            mViews += additionalImpressions.getViews();
96            mStreak += additionalImpressions.getStreak();
97            mDismissals += additionalImpressions.getDismissals();
98        }
99    }
100
101    public void incrementViews() {
102        mViews++;
103    }
104
105    public void resetStreak() {
106        mStreak = 0;
107    }
108
109    public boolean shouldTriggerBlock() {
110        if (getViews() == 0) {
111            return false;
112        }
113        if (DEBUG) {
114            Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak());
115        }
116        return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit
117                && getStreak() > mStreakLimit;
118    }
119
120    @Override
121    public void writeToParcel(Parcel dest, int flags) {
122        dest.writeInt(mDismissals);
123        dest.writeInt(mViews);
124        dest.writeInt(mStreak);
125        dest.writeFloat(mDismissToViewRatioLimit);
126        dest.writeInt(mStreakLimit);
127    }
128
129    @Override
130    public int describeContents() {
131        return 0;
132    }
133
134    public static final Creator<ChannelImpressions> CREATOR = new Creator<ChannelImpressions>() {
135        @Override
136        public ChannelImpressions createFromParcel(Parcel in) {
137            return new ChannelImpressions(in);
138        }
139
140        @Override
141        public ChannelImpressions[] newArray(int size) {
142            return new ChannelImpressions[size];
143        }
144    };
145
146    @Override
147    public boolean equals(Object o) {
148        if (this == o) return true;
149        if (o == null || getClass() != o.getClass()) return false;
150
151        ChannelImpressions that = (ChannelImpressions) o;
152
153        if (mDismissals != that.mDismissals) return false;
154        if (mViews != that.mViews) return false;
155        return mStreak == that.mStreak;
156    }
157
158    @Override
159    public int hashCode() {
160        int result = mDismissals;
161        result = 31 * result + mViews;
162        result = 31 * result + mStreak;
163        return result;
164    }
165
166    @Override
167    public String toString() {
168        final StringBuilder sb = new StringBuilder("ChannelImpressions{");
169        sb.append("mDismissals=").append(mDismissals);
170        sb.append(", mViews=").append(mViews);
171        sb.append(", mStreak=").append(mStreak);
172        sb.append(", thresholds=(").append(mDismissToViewRatioLimit);
173        sb.append(",").append(mStreakLimit);
174        sb.append(")}");
175        return sb.toString();
176    }
177
178    protected void populateFromXml(XmlPullParser parser) {
179        mDismissals = safeInt(parser, ATT_DISMISSALS, 0);
180        mStreak = safeInt(parser, ATT_STREAK, 0);
181        mViews = safeInt(parser, ATT_VIEWS, 0);
182    }
183
184    protected void writeXml(XmlSerializer out) throws IOException {
185        if (mDismissals != 0) {
186            out.attribute(null, ATT_DISMISSALS, String.valueOf(mDismissals));
187        }
188        if (mStreak != 0) {
189            out.attribute(null, ATT_STREAK, String.valueOf(mStreak));
190        }
191        if (mViews != 0) {
192            out.attribute(null, ATT_VIEWS, String.valueOf(mViews));
193        }
194    }
195
196    private static int safeInt(XmlPullParser parser, String att, int defValue) {
197        final String val = parser.getAttributeValue(null, att);
198        return tryParseInt(val, defValue);
199    }
200
201    private static int tryParseInt(String value, int defValue) {
202        if (TextUtils.isEmpty(value)) return defValue;
203        try {
204            return Integer.parseInt(value);
205        } catch (NumberFormatException e) {
206            return defValue;
207        }
208    }
209}
210