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 package android.content; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.view.contentcapture.ContentCaptureManager; 23 24 import com.android.internal.util.Preconditions; 25 26 import java.io.PrintWriter; 27 28 /** 29 * An identifier for an unique state (locus) in the application. Should be stable across reboots and 30 * backup / restore. 31 * 32 * <p>Locus is a new concept introduced on 33 * {@link android.os.Build.VERSION_CODES#Q Android Q} and it lets the intelligence service provided 34 * by the Android System to correlate state between different subsystems such as content capture, 35 * shortcuts, and notifications. 36 * 37 * <p>For example, if your app provides an activity representing a chat between 2 users 38 * (say {@code A} and {@code B}, this chat state could be represented by: 39 * 40 * <pre><code> 41 * LocusId chatId = new LocusId("Chat_A_B"); 42 * </code></pre> 43 * 44 * <p>And then you should use that {@code chatId} by: 45 * 46 * <ul> 47 * <li>Setting it in the chat notification (through 48 * {@link android.app.Notification.Builder#setLocusId(LocusId) 49 * Notification.Builder.setLocusId(chatId)}). 50 * <li>Setting it into the {@link android.content.pm.ShortcutInfo} (through 51 * {@link android.content.pm.ShortcutInfo.Builder#setLocusId(LocusId) 52 * ShortcutInfo.Builder.setLocusId(chatId)}), if you provide a launcher shortcut for that chat 53 * conversation. 54 * <li>Associating it with the {@link android.view.contentcapture.ContentCaptureContext} of the 55 * root view of the chat conversation activity (through 56 * {@link android.view.View#getContentCaptureSession()}, then 57 * {@link android.view.contentcapture.ContentCaptureContext.Builder 58 * new ContentCaptureContext.Builder(chatId).build()} and 59 * {@link android.view.contentcapture.ContentCaptureSession#setContentCaptureContext( 60 * android.view.contentcapture.ContentCaptureContext)} - see {@link ContentCaptureManager} 61 * for more info about content capture). 62 * <li>Configuring your app to launch the chat conversation through the 63 * {@link Intent#ACTION_VIEW_LOCUS} intent. 64 * </ul> 65 * 66 * NOTE: The LocusId is only used by an on-device intelligence service provided by the Android 67 * System, see {@link ContentCaptureManager} for more details. 68 */ 69 public final class LocusId implements Parcelable { 70 71 private final String mId; 72 73 /** 74 * Default constructor. 75 * 76 * @throws IllegalArgumentException if {@code id} is empty or {@code null}. 77 */ LocusId(@onNull String id)78 public LocusId(@NonNull String id) { 79 mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty"); 80 } 81 82 /** 83 * Gets the canonical {@code id} associated with the locus. 84 */ 85 @NonNull getId()86 public String getId() { 87 return mId; 88 } 89 90 @Override hashCode()91 public int hashCode() { 92 final int prime = 31; 93 int result = 1; 94 result = prime * result + ((mId == null) ? 0 : mId.hashCode()); 95 return result; 96 } 97 98 @Override equals(@ullable Object obj)99 public boolean equals(@Nullable Object obj) { 100 if (this == obj) return true; 101 if (obj == null) return false; 102 if (getClass() != obj.getClass()) return false; 103 final LocusId other = (LocusId) obj; 104 if (mId == null) { 105 if (other.mId != null) return false; 106 } else { 107 if (!mId.equals(other.mId)) return false; 108 } 109 return true; 110 } 111 112 @Override toString()113 public String toString() { 114 return "LocusId[" + getSanitizedId() + "]"; 115 } 116 117 /** @hide */ dump(@onNull PrintWriter pw)118 public void dump(@NonNull PrintWriter pw) { 119 pw.print("id:"); pw.println(getSanitizedId()); 120 } 121 122 @NonNull getSanitizedId()123 private String getSanitizedId() { 124 final int size = mId.length(); 125 return size + "_chars"; 126 } 127 128 @Override describeContents()129 public int describeContents() { 130 return 0; 131 } 132 133 @Override writeToParcel(Parcel parcel, int flags)134 public void writeToParcel(Parcel parcel, int flags) { 135 parcel.writeString(mId); 136 } 137 138 public static final @NonNull Parcelable.Creator<LocusId> CREATOR = 139 new Parcelable.Creator<LocusId>() { 140 141 @NonNull 142 @Override 143 public LocusId createFromParcel(Parcel parcel) { 144 return new LocusId(parcel.readString()); 145 } 146 147 @NonNull 148 @Override 149 public LocusId[] newArray(int size) { 150 return new LocusId[size]; 151 } 152 }; 153 } 154