1 /* 2 * Copyright (C) 2018 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.google.android.setupcompat.logging; 18 19 import static com.google.android.setupcompat.internal.Validations.assertLengthInRange; 20 21 import android.app.Activity; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import androidx.annotation.NonNull; 26 import com.google.android.setupcompat.internal.Preconditions; 27 import com.google.android.setupcompat.util.ObjectUtils; 28 import java.util.regex.Pattern; 29 30 /** 31 * A metric key represents a validated “string key” and a "screen name" that is associated with the 32 * values reported by the API consumer. 33 */ 34 public final class MetricKey implements Parcelable { 35 36 private static final String METRIC_KEY_BUNDLE_NAME_KEY = "MetricKey_name"; 37 private static final String METRIC_KEY_BUNDLE_SCREEN_NAME_KEY = "MetricKey_screenName"; 38 private static final String METRIC_KEY_BUNDLE_VERSION = "MetricKey_version"; 39 private static final int VERSION = 1; 40 41 /** 42 * Creates a new instance of MetricKey. 43 * 44 * @param name metric name to identify what we log 45 * @param activity activity of metric screen, uses to generate screenName 46 */ get(@onNull String name, @NonNull Activity activity)47 public static MetricKey get(@NonNull String name, @NonNull Activity activity) { 48 String screenName = activity.getComponentName().getClassName(); 49 assertLengthInRange(name, "MetricKey.name", MIN_METRIC_KEY_LENGTH, MAX_METRIC_KEY_LENGTH); 50 Preconditions.checkArgument( 51 METRIC_KEY_PATTERN.matcher(name).matches(), 52 "Invalid MetricKey, only alpha numeric characters are allowed."); 53 return new MetricKey(name, screenName); 54 } 55 56 /** 57 * Creates a new instance of MetricKey. 58 * 59 * <p>NOTE: 60 * 61 * <ul> 62 * <li>Length of {@code name} should be in range of 5-30 characters, only alpha numeric 63 * characters are allowed. 64 * <li>Length of {@code screenName} should be in range of 5-50 characters, only alpha numeric 65 * characters are allowed. 66 * </ul> 67 */ get(@onNull String name, @NonNull String screenName)68 public static MetricKey get(@NonNull String name, @NonNull String screenName) { 69 // We only checked the length of customized screen name, by the reason if the screenName match 70 // to the class name skip check it 71 if (!SCREEN_COMPONENTNAME_PATTERN.matcher(screenName).matches()) { 72 assertLengthInRange( 73 screenName, "MetricKey.screenName", MIN_SCREEN_NAME_LENGTH, MAX_SCREEN_NAME_LENGTH); 74 Preconditions.checkArgument( 75 SCREEN_NAME_PATTERN.matcher(screenName).matches(), 76 "Invalid ScreenName, only alpha numeric characters are allowed."); 77 } 78 79 assertLengthInRange(name, "MetricKey.name", MIN_METRIC_KEY_LENGTH, MAX_METRIC_KEY_LENGTH); 80 Preconditions.checkArgument( 81 METRIC_KEY_PATTERN.matcher(name).matches(), 82 "Invalid MetricKey, only alpha numeric characters are allowed."); 83 84 return new MetricKey(name, screenName); 85 } 86 87 /** Converts {@link MetricKey} into {@link Bundle}. */ fromMetricKey(MetricKey metricKey)88 public static Bundle fromMetricKey(MetricKey metricKey) { 89 Preconditions.checkNotNull(metricKey, "MetricKey cannot be null."); 90 Bundle bundle = new Bundle(); 91 bundle.putInt(METRIC_KEY_BUNDLE_VERSION, VERSION); 92 bundle.putString(METRIC_KEY_BUNDLE_NAME_KEY, metricKey.name()); 93 bundle.putString(METRIC_KEY_BUNDLE_SCREEN_NAME_KEY, metricKey.screenName()); 94 return bundle; 95 } 96 97 /** Converts {@link Bundle} into {@link MetricKey}. */ toMetricKey(Bundle bundle)98 public static MetricKey toMetricKey(Bundle bundle) { 99 Preconditions.checkNotNull(bundle, "Bundle cannot be null"); 100 return MetricKey.get( 101 bundle.getString(METRIC_KEY_BUNDLE_NAME_KEY), 102 bundle.getString(METRIC_KEY_BUNDLE_SCREEN_NAME_KEY)); 103 } 104 105 public static final Creator<MetricKey> CREATOR = 106 new Creator<MetricKey>() { 107 @Override 108 public MetricKey createFromParcel(Parcel in) { 109 return new MetricKey(in.readString(), in.readString()); 110 } 111 112 @Override 113 public MetricKey[] newArray(int size) { 114 return new MetricKey[size]; 115 } 116 }; 117 118 /** Returns the name of the metric key. */ name()119 public String name() { 120 return name; 121 } 122 123 /** Returns the name of the metric key. */ screenName()124 public String screenName() { 125 return screenName; 126 } 127 128 @Override describeContents()129 public int describeContents() { 130 return 0; 131 } 132 133 @Override writeToParcel(Parcel parcel, int i)134 public void writeToParcel(Parcel parcel, int i) { 135 parcel.writeString(name); 136 parcel.writeString(screenName); 137 } 138 139 @Override equals(Object o)140 public boolean equals(Object o) { 141 if (this == o) { 142 return true; 143 } 144 if (!(o instanceof MetricKey)) { 145 return false; 146 } 147 MetricKey metricKey = (MetricKey) o; 148 return ObjectUtils.equals(name, metricKey.name) 149 && ObjectUtils.equals(screenName, metricKey.screenName); 150 } 151 152 @Override hashCode()153 public int hashCode() { 154 return ObjectUtils.hashCode(name, screenName); 155 } 156 MetricKey(String name, String screenName)157 private MetricKey(String name, String screenName) { 158 this.name = name; 159 this.screenName = screenName; 160 } 161 162 private final String name; 163 private final String screenName; 164 165 private static final int MIN_SCREEN_NAME_LENGTH = 5; 166 private static final int MIN_METRIC_KEY_LENGTH = 5; 167 private static final int MAX_SCREEN_NAME_LENGTH = 50; 168 private static final int MAX_METRIC_KEY_LENGTH = 30; 169 private static final Pattern METRIC_KEY_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]+"); 170 private static final Pattern SCREEN_COMPONENTNAME_PATTERN = 171 Pattern.compile("^([a-z]+[.])+[A-Z][a-zA-Z0-9]+"); 172 private static final Pattern SCREEN_NAME_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]+"); 173 } 174