1 /* 2 * Copyright (C) 2019 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.settings.homepage.contextualcards.logging; 18 19 import android.util.Log; 20 21 import androidx.slice.widget.EventInfo; 22 23 import com.android.settings.homepage.contextualcards.ContextualCard; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 28 /** 29 * Utils of building contextual card to string, and parse string back to {@link CardLog} 30 */ 31 public class ContextualCardLogUtils { 32 33 private static final String TAG = "ContextualCardLogUtils"; 34 35 private static final class TapTarget { 36 static int TARGET_DEFAULT = 0; 37 static int TARGET_TITLE = 1; 38 static int TARGET_TOGGLE = 2; 39 static int TARGET_SLIDER = 3; 40 } 41 42 /** 43 * Log data for a general contextual card event 44 */ 45 public static class CardLog { 46 private final String mSliceUri; 47 private final double mRankingScore; 48 CardLog(Builder builder)49 public CardLog(Builder builder) { 50 mSliceUri = builder.mSliceUri; 51 mRankingScore = builder.mRankingScore; 52 } 53 getSliceUri()54 public String getSliceUri() { 55 return mSliceUri; 56 } 57 getRankingScore()58 public double getRankingScore() { 59 return mRankingScore; 60 } 61 62 public static class Builder { 63 private String mSliceUri; 64 private double mRankingScore; 65 setSliceUri(String sliceUri)66 public Builder setSliceUri(String sliceUri) { 67 mSliceUri = sliceUri; 68 return this; 69 } 70 setRankingScore(double rankingScore)71 public Builder setRankingScore(double rankingScore) { 72 mRankingScore = rankingScore; 73 return this; 74 } build()75 public CardLog build() { 76 return new CardLog(this); 77 } 78 } 79 } 80 81 /** 82 * Log data for a contextual card click event 83 */ 84 public static class CardClickLog extends CardLog { 85 private final int mSliceRow; 86 private final int mSliceTapTarget; 87 private final int mUiPosition; 88 CardClickLog(Builder builder)89 public CardClickLog(Builder builder) { 90 super(builder); 91 mSliceRow = builder.mSliceRow; 92 mSliceTapTarget = builder.mSliceTapTarget; 93 mUiPosition = builder.mUiPosition; 94 } 95 getSliceRow()96 public int getSliceRow() { 97 return mSliceRow; 98 } 99 getSliceTapTarget()100 public int getSliceTapTarget() { 101 return mSliceTapTarget; 102 } 103 getUiPosition()104 public int getUiPosition() { 105 return mUiPosition; 106 } 107 108 public static class Builder extends CardLog.Builder { 109 private int mSliceRow; 110 private int mSliceTapTarget; 111 private int mUiPosition; 112 setSliceRow(int sliceRow)113 public Builder setSliceRow(int sliceRow) { 114 mSliceRow = sliceRow; 115 return this; 116 } 117 setSliceTapTarget(int sliceTapTarget)118 public Builder setSliceTapTarget(int sliceTapTarget) { 119 mSliceTapTarget = sliceTapTarget; 120 return this; 121 } 122 setUiPosition(int uiPosition)123 public Builder setUiPosition(int uiPosition) { 124 mUiPosition = uiPosition; 125 return this; 126 } 127 @Override build()128 public CardClickLog build() { 129 return new CardClickLog(this); 130 } 131 } 132 } 133 134 /** 135 * Serialize {@link ContextualCard} click event to string 136 * 137 * @param card Clicked Contextual card. 138 * @param sliceRow A Slice can contains multiple row, which row are we clicked 139 * @param tapTarget Integer value of {@link TapTarget} 140 * @param uiPosition Contextual card position in Listview 141 */ buildCardClickLog(ContextualCard card, int sliceRow, int tapTarget, int uiPosition)142 public static String buildCardClickLog(ContextualCard card, int sliceRow, int tapTarget, 143 int uiPosition) { 144 final StringBuilder log = new StringBuilder(); 145 log.append(card.getTextSliceUri()).append("|") 146 .append(card.getRankingScore()).append("|") 147 .append(sliceRow).append("|") 148 .append(actionTypeToTapTarget(tapTarget)).append("|") 149 .append(uiPosition); 150 return log.toString(); 151 } 152 153 /** 154 * Parse string to a {@link CardClickLog} 155 */ parseCardClickLog(String clickLog)156 public static CardClickLog parseCardClickLog(String clickLog) { 157 if (clickLog != null) { 158 final String[] parts = clickLog.split("\\|"); 159 if (parts.length < 5) { 160 return null; 161 } 162 try { 163 final CardClickLog.Builder builder = new CardClickLog.Builder(); 164 builder.setSliceRow(Integer.parseInt(parts[2])) 165 .setSliceTapTarget(Integer.parseInt(parts[3])) 166 .setUiPosition(Integer.parseInt(parts[4])) 167 .setSliceUri(parts[0]) 168 .setRankingScore(Double.parseDouble(parts[1])); 169 return builder.build(); 170 } catch (Exception e) { 171 Log.e(TAG, "error parsing log", e); 172 return null; 173 } 174 } 175 return null; 176 } 177 178 /** 179 * Serialize {@link ContextualCard} to string 180 * 181 * @param card Contextual card. 182 */ buildCardDismissLog(ContextualCard card)183 public static String buildCardDismissLog(ContextualCard card) { 184 final StringBuilder log = new StringBuilder(); 185 log.append(card.getTextSliceUri()) 186 .append("|") 187 .append(card.getRankingScore()); 188 return log.toString(); 189 } 190 191 /** 192 * Parse string to a {@link CardLog} 193 */ parseCardDismissLog(String dismissLog)194 public static CardLog parseCardDismissLog(String dismissLog) { 195 if (dismissLog != null) { 196 final String[] parts = dismissLog.split("\\|"); 197 if (parts.length < 2) { 198 return null; 199 } 200 try { 201 final CardLog.Builder builder = new CardLog.Builder(); 202 builder.setSliceUri(parts[0]) 203 .setRankingScore(Double.parseDouble(parts[1])); 204 return builder.build(); 205 } catch (Exception e) { 206 Log.e(TAG, "error parsing log", e); 207 return null; 208 } 209 } 210 return null; 211 } 212 213 /** 214 * Serialize List of {@link ContextualCard} to string 215 */ buildCardListLog(List<ContextualCard> cards)216 public static String buildCardListLog(List<ContextualCard> cards) { 217 final StringBuilder log = new StringBuilder(); 218 log.append(cards.size()); 219 for (ContextualCard card : cards) { 220 log.append("|").append(card.getTextSliceUri()) 221 .append("|").append(card.getRankingScore()); 222 } 223 return log.toString(); 224 } 225 226 /** 227 * Parse string to a List of {@link CardLog} 228 */ parseCardListLog(String listLog)229 public static List<CardLog> parseCardListLog(String listLog) { 230 final List<CardLog> logList = new ArrayList<>(); 231 try { 232 final String[] parts = listLog.split("\\|"); 233 if (Integer.parseInt(parts[0]) < 0) { 234 return logList; 235 } 236 final int size = parts.length; 237 for (int i = 1; i < size; ) { 238 final CardLog.Builder builder = new CardLog.Builder(); 239 builder.setSliceUri(parts[i++]) 240 .setRankingScore(Double.parseDouble(parts[i++])); 241 logList.add(builder.build()); 242 } 243 } catch (Exception e) { 244 Log.e(TAG, "error parsing log", e); 245 return logList; 246 } 247 return logList; 248 } 249 actionTypeToTapTarget(int actionType)250 public static int actionTypeToTapTarget(int actionType) { 251 switch (actionType) { 252 case EventInfo.ACTION_TYPE_CONTENT: 253 return TapTarget.TARGET_TITLE; 254 case EventInfo.ACTION_TYPE_TOGGLE: 255 return TapTarget.TARGET_TOGGLE; 256 case EventInfo.ACTION_TYPE_SLIDER: 257 return TapTarget.TARGET_SLIDER; 258 default: 259 Log.w(TAG, "unknown type " + actionType); 260 return TapTarget.TARGET_DEFAULT; 261 } 262 } 263 } 264