Merge "Add a status API for conversations"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9e4b9c84da
@@ -7826,6 +7826,52 @@ package android.app.job {
|
||||
|
||||
}
|
||||
|
||||
package android.app.people {
|
||||
|
||||
public final class ConversationStatus implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public int getActivity();
|
||||
method public int getAvailability();
|
||||
method @Nullable public CharSequence getDescription();
|
||||
method public long getEndTimeMillis();
|
||||
method @Nullable public android.graphics.drawable.Icon getIcon();
|
||||
method @NonNull public String getId();
|
||||
method public long getStartTimeMillis();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field public static final int ACTIVITY_ANNIVERSARY = 2; // 0x2
|
||||
field public static final int ACTIVITY_BIRTHDAY = 1; // 0x1
|
||||
field public static final int ACTIVITY_GAME = 5; // 0x5
|
||||
field public static final int ACTIVITY_LOCATION = 6; // 0x6
|
||||
field public static final int ACTIVITY_MEDIA = 4; // 0x4
|
||||
field public static final int ACTIVITY_NEW_STORY = 3; // 0x3
|
||||
field public static final int ACTIVITY_OTHER = 0; // 0x0
|
||||
field public static final int ACTIVITY_UPCOMING_BIRTHDAY = 7; // 0x7
|
||||
field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
|
||||
field public static final int AVAILABILITY_BUSY = 1; // 0x1
|
||||
field public static final int AVAILABILITY_OFFLINE = 2; // 0x2
|
||||
field public static final int AVAILABILITY_UNKNOWN = -1; // 0xffffffff
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.app.people.ConversationStatus> CREATOR;
|
||||
}
|
||||
|
||||
public static final class ConversationStatus.Builder {
|
||||
ctor public ConversationStatus.Builder(@NonNull String, @NonNull int);
|
||||
method @NonNull public android.app.people.ConversationStatus build();
|
||||
method @NonNull public android.app.people.ConversationStatus.Builder setAvailability(int);
|
||||
method @NonNull public android.app.people.ConversationStatus.Builder setDescription(@Nullable CharSequence);
|
||||
method @NonNull public android.app.people.ConversationStatus.Builder setEndTimeMillis(long);
|
||||
method @NonNull public android.app.people.ConversationStatus.Builder setIcon(@Nullable android.graphics.drawable.Icon);
|
||||
method @NonNull public android.app.people.ConversationStatus.Builder setStartTimeMillis(long);
|
||||
}
|
||||
|
||||
public final class PeopleManager {
|
||||
method public void addOrUpdateStatus(@NonNull String, @NonNull android.app.people.ConversationStatus);
|
||||
method public void clearStatus(@NonNull String, @NonNull String);
|
||||
method public void clearStatuses(@NonNull String);
|
||||
method @NonNull public java.util.List<android.app.people.ConversationStatus> getStatuses(@NonNull String);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.app.role {
|
||||
|
||||
public final class RoleManager {
|
||||
@@ -10315,6 +10361,7 @@ package android.content {
|
||||
field public static final String NFC_SERVICE = "nfc";
|
||||
field public static final String NOTIFICATION_SERVICE = "notification";
|
||||
field public static final String NSD_SERVICE = "servicediscovery";
|
||||
field public static final String PEOPLE_SERVICE = "people";
|
||||
field public static final String POWER_SERVICE = "power";
|
||||
field public static final String PRINT_SERVICE = "print";
|
||||
field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.app.blob.BlobStoreManagerFrameworkInitializer;
|
||||
import android.app.contentsuggestions.ContentSuggestionsManager;
|
||||
import android.app.contentsuggestions.IContentSuggestionsManager;
|
||||
import android.app.job.JobSchedulerFrameworkInitializer;
|
||||
import android.app.people.PeopleManager;
|
||||
import android.app.prediction.AppPredictionManager;
|
||||
import android.app.role.RoleControllerManager;
|
||||
import android.app.role.RoleManager;
|
||||
@@ -586,6 +587,13 @@ public final class SystemServiceRegistry {
|
||||
return new NsdManager(ctx.getOuterContext(), service);
|
||||
}});
|
||||
|
||||
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
|
||||
new CachedServiceFetcher<PeopleManager>() {
|
||||
@Override
|
||||
public PeopleManager createService(ContextImpl ctx) throws ServiceNotFoundException {
|
||||
return new PeopleManager(ctx);
|
||||
}});
|
||||
|
||||
registerService(Context.POWER_SERVICE, PowerManager.class,
|
||||
new CachedServiceFetcher<PowerManager>() {
|
||||
@Override
|
||||
|
||||
19
core/java/android/app/people/ConversationStatus.aidl
Normal file
19
core/java/android/app/people/ConversationStatus.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.people;
|
||||
|
||||
parcelable ConversationStatus;
|
||||
240
core/java/android/app/people/ConversationStatus.java
Normal file
240
core/java/android/app/people/ConversationStatus.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.people;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class ConversationStatus implements Parcelable {
|
||||
private static final String TAG = "ConversationStatus";
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "ACTIVITY_" }, value = {
|
||||
ACTIVITY_OTHER,
|
||||
ACTIVITY_BIRTHDAY,
|
||||
ACTIVITY_ANNIVERSARY,
|
||||
ACTIVITY_NEW_STORY,
|
||||
ACTIVITY_MEDIA,
|
||||
ACTIVITY_GAME,
|
||||
ACTIVITY_LOCATION,
|
||||
ACTIVITY_UPCOMING_BIRTHDAY
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ActivityType {}
|
||||
|
||||
public static final int ACTIVITY_OTHER = 0;
|
||||
public static final int ACTIVITY_BIRTHDAY = 1;
|
||||
public static final int ACTIVITY_ANNIVERSARY = 2;
|
||||
public static final int ACTIVITY_NEW_STORY = 3;
|
||||
public static final int ACTIVITY_MEDIA = 4;
|
||||
public static final int ACTIVITY_GAME = 5;
|
||||
public static final int ACTIVITY_LOCATION = 6;
|
||||
public static final int ACTIVITY_UPCOMING_BIRTHDAY = 7;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "AVAILABILITY_" }, value = {
|
||||
AVAILABILITY_UNKNOWN,
|
||||
AVAILABILITY_AVAILABLE,
|
||||
AVAILABILITY_BUSY,
|
||||
AVAILABILITY_OFFLINE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Availability {}
|
||||
|
||||
public static final int AVAILABILITY_UNKNOWN = -1;
|
||||
public static final int AVAILABILITY_AVAILABLE = 0;
|
||||
public static final int AVAILABILITY_BUSY = 1;
|
||||
public static final int AVAILABILITY_OFFLINE = 2;
|
||||
|
||||
private final String mId;
|
||||
private final int mActivity;
|
||||
|
||||
private int mAvailability;
|
||||
private CharSequence mDescription;
|
||||
private Icon mIcon;
|
||||
private long mStartTimeMs;
|
||||
private long mEndTimeMs;
|
||||
|
||||
private ConversationStatus(Builder b) {
|
||||
mId = b.mId;
|
||||
mActivity = b.mActivity;
|
||||
mAvailability = b.mAvailability;
|
||||
mDescription = b.mDescription;
|
||||
mIcon = b.mIcon;
|
||||
mStartTimeMs = b.mStartTimeMs;
|
||||
mEndTimeMs = b.mEndTimeMs;
|
||||
}
|
||||
|
||||
private ConversationStatus(Parcel p) {
|
||||
mId = p.readString();
|
||||
mActivity = p.readInt();
|
||||
mAvailability = p.readInt();
|
||||
mDescription = p.readCharSequence();
|
||||
mIcon = p.readParcelable(Icon.class.getClassLoader());
|
||||
mStartTimeMs = p.readLong();
|
||||
mEndTimeMs = p.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mId);
|
||||
dest.writeInt(mActivity);
|
||||
dest.writeInt(mAvailability);
|
||||
dest.writeCharSequence(mDescription);
|
||||
dest.writeParcelable(mIcon, flags);
|
||||
dest.writeLong(mStartTimeMs);
|
||||
dest.writeLong(mEndTimeMs);
|
||||
}
|
||||
|
||||
public @NonNull String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public @ActivityType int getActivity() {
|
||||
return mActivity;
|
||||
}
|
||||
|
||||
public @Availability
|
||||
int getAvailability() {
|
||||
return mAvailability;
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
CharSequence getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public @Nullable Icon getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
public long getStartTimeMillis() {
|
||||
return mStartTimeMs;
|
||||
}
|
||||
|
||||
public long getEndTimeMillis() {
|
||||
return mEndTimeMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ConversationStatus that = (ConversationStatus) o;
|
||||
return mActivity == that.mActivity &&
|
||||
mAvailability == that.mAvailability &&
|
||||
mStartTimeMs == that.mStartTimeMs &&
|
||||
mEndTimeMs == that.mEndTimeMs &&
|
||||
mId.equals(that.mId) &&
|
||||
Objects.equals(mDescription, that.mDescription) &&
|
||||
Objects.equals(mIcon, that.mIcon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mId, mActivity, mAvailability, mDescription, mIcon, mStartTimeMs,
|
||||
mEndTimeMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConversationStatus{" +
|
||||
"mId='" + mId + '\'' +
|
||||
", mActivity=" + mActivity +
|
||||
", mAvailability=" + mAvailability +
|
||||
", mDescription=" + mDescription +
|
||||
", mIcon=" + mIcon +
|
||||
", mStartTimeMs=" + mStartTimeMs +
|
||||
", mEndTimeMs=" + mEndTimeMs +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<ConversationStatus> CREATOR
|
||||
= new Creator<ConversationStatus>() {
|
||||
public ConversationStatus createFromParcel(Parcel source) {
|
||||
return new ConversationStatus(source);
|
||||
}
|
||||
|
||||
public ConversationStatus[] newArray(int size) {
|
||||
return new ConversationStatus[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static final class Builder {
|
||||
final String mId;
|
||||
final int mActivity;
|
||||
int mAvailability = AVAILABILITY_UNKNOWN;
|
||||
CharSequence mDescription;
|
||||
Icon mIcon;
|
||||
long mStartTimeMs = -1;
|
||||
long mEndTimeMs = -1;
|
||||
|
||||
/**
|
||||
* Creates a new builder.
|
||||
*
|
||||
* @param id The unique id for this status
|
||||
* @param activity The type of status
|
||||
*/
|
||||
public Builder(@NonNull String id, @ActivityType @NonNull int activity) {
|
||||
mId = id;
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
|
||||
public @NonNull Builder setAvailability(@Availability int availability) {
|
||||
mAvailability = availability;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Builder setDescription(@Nullable CharSequence description) {
|
||||
mDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Builder setIcon(@Nullable Icon icon) {
|
||||
mIcon = icon;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Builder setStartTimeMillis(long startTimeMs) {
|
||||
mStartTimeMs = startTimeMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Builder setEndTimeMillis(long endTimeMs) {
|
||||
mEndTimeMs = endTimeMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull ConversationStatus build() {
|
||||
return new ConversationStatus(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.app.people;
|
||||
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.net.Uri;
|
||||
import android.os.IBinder;
|
||||
@@ -45,4 +46,9 @@ interface IPeopleManager {
|
||||
* conversation can't be found or no interactions have been recorded, returns 0L.
|
||||
*/
|
||||
long getLastInteraction(in String packageName, int userId, in String shortcutId);
|
||||
|
||||
void addOrUpdateStatus(in String packageName, int userId, in String conversationId, in ConversationStatus status);
|
||||
void clearStatus(in String packageName, int userId, in String conversationId, in String statusId);
|
||||
void clearStatuses(in String packageName, int userId, in String conversationId);
|
||||
ParceledListSlice getStatuses(in String packageName, int userId, in String conversationId);
|
||||
}
|
||||
|
||||
138
core/java/android/app/people/PeopleManager.java
Normal file
138
core/java/android/app/people/PeopleManager.java
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.people;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class allows interaction with conversation and people data.
|
||||
*/
|
||||
@SystemService(Context.PEOPLE_SERVICE)
|
||||
public final class PeopleManager {
|
||||
|
||||
private static final String LOG_TAG = PeopleManager.class.getSimpleName();
|
||||
|
||||
@NonNull
|
||||
private final Context mContext;
|
||||
|
||||
@NonNull
|
||||
private final IPeopleManager mService;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public PeopleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException {
|
||||
mContext = context;
|
||||
mService = IPeopleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
|
||||
Context.PEOPLE_SERVICE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets or updates a {@link ConversationStatus} for a conversation.
|
||||
*
|
||||
* <p>Statuses are meant to represent current information about the conversation. Like
|
||||
* notifications, they are transient and are not persisted beyond a reboot, nor are they
|
||||
* backed up and restored.</p>
|
||||
* <p>If the provided conversation shortcut is not already pinned, or cached by the system,
|
||||
* it will remain cached as long as the status is active.</p>
|
||||
*
|
||||
* @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
|
||||
* conversation that has an active status
|
||||
* @param status the current status for the given conversation
|
||||
*
|
||||
* @return whether the role is available in the system
|
||||
*/
|
||||
public void addOrUpdateStatus(@NonNull String conversationId,
|
||||
@NonNull ConversationStatus status) {
|
||||
Preconditions.checkStringNotEmpty(conversationId);
|
||||
Objects.requireNonNull(status);
|
||||
try {
|
||||
mService.addOrUpdateStatus(
|
||||
mContext.getPackageName(), mContext.getUserId(), conversationId, status);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublishes a given status from the given conversation.
|
||||
*
|
||||
* @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
|
||||
* conversation that has an active status
|
||||
* @param statusId the {@link ConversationStatus#getId() id} of a published status for the given
|
||||
* conversation
|
||||
*/
|
||||
public void clearStatus(@NonNull String conversationId, @NonNull String statusId) {
|
||||
Preconditions.checkStringNotEmpty(conversationId);
|
||||
Preconditions.checkStringNotEmpty(statusId);
|
||||
try {
|
||||
mService.clearStatus(
|
||||
mContext.getPackageName(), mContext.getUserId(), conversationId, statusId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all published statuses for the given conversation.
|
||||
*
|
||||
* @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
|
||||
* conversation that has one or more active statuses
|
||||
*/
|
||||
public void clearStatuses(@NonNull String conversationId) {
|
||||
Preconditions.checkStringNotEmpty(conversationId);
|
||||
try {
|
||||
mService.clearStatuses(
|
||||
mContext.getPackageName(), mContext.getUserId(), conversationId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the currently published statuses for a given conversation.
|
||||
*
|
||||
* @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
|
||||
* conversation that has one or more active statuses
|
||||
*/
|
||||
public @NonNull List<ConversationStatus> getStatuses(@NonNull String conversationId) {
|
||||
try {
|
||||
final ParceledListSlice<ConversationStatus> parceledList
|
||||
= mService.getStatuses(
|
||||
mContext.getPackageName(), mContext.getUserId(), conversationId);
|
||||
if (parceledList != null) {
|
||||
return parceledList.getList();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import android.app.ActivityManager;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.VrManager;
|
||||
import android.app.people.PeopleManager;
|
||||
import android.app.time.TimeManager;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -5311,10 +5312,10 @@ public abstract class Context {
|
||||
public static final String SMS_SERVICE = "sms";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to access people service.
|
||||
* Use with {@link #getSystemService(String)} to access a {@link PeopleManager} to interact
|
||||
* with your published conversations.
|
||||
*
|
||||
* @see #getSystemService(String)
|
||||
* @hide
|
||||
*/
|
||||
public static final String PEOPLE_SERVICE = "people";
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ package com.android.server.people;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.people.ConversationChannel;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.people.IPeopleManager;
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.app.prediction.AppPredictionSessionId;
|
||||
@@ -27,6 +29,7 @@ import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.IPredictionCallback;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Binder;
|
||||
import android.os.CancellationSignal;
|
||||
@@ -38,9 +41,11 @@ import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.people.data.DataManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@@ -54,6 +59,8 @@ public class PeopleService extends SystemService {
|
||||
|
||||
private final DataManager mDataManager;
|
||||
|
||||
private PackageManagerInternal mPackageManagerInternal;
|
||||
|
||||
/**
|
||||
* Initializes the system service.
|
||||
*
|
||||
@@ -83,6 +90,7 @@ public class PeopleService extends SystemService {
|
||||
publishBinderService(Context.PEOPLE_SERVICE, mService);
|
||||
}
|
||||
publishLocalService(PeopleServiceInternal.class, new LocalService());
|
||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,6 +120,26 @@ public class PeopleService extends SystemService {
|
||||
return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || uid == Process.ROOT_UID;
|
||||
}
|
||||
|
||||
private int handleIncomingUser(int userId) {
|
||||
try {
|
||||
return ActivityManager.getService().handleIncomingUser(
|
||||
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
|
||||
} catch (RemoteException re) {
|
||||
// Shouldn't happen, local.
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
private void checkCallerIsSameApp(String pkg) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingUserId = UserHandle.getUserId(callingUid);
|
||||
|
||||
if (mPackageManagerInternal.getPackageUid(pkg, /*flags=*/ 0,
|
||||
callingUserId) != callingUid) {
|
||||
throw new SecurityException("Calling uid " + callingUid + " cannot query events"
|
||||
+ "for package " + pkg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces that only the system, root UID or SystemUI can make certain calls.
|
||||
@@ -154,6 +182,40 @@ public class PeopleService extends SystemService {
|
||||
enforceSystemRootOrSystemUI(getContext(), "get last interaction");
|
||||
return mDataManager.getLastInteraction(packageName, userId, shortcutId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOrUpdateStatus(String packageName, int userId, String conversationId,
|
||||
ConversationStatus status) {
|
||||
handleIncomingUser(userId);
|
||||
checkCallerIsSameApp(packageName);
|
||||
mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearStatus(String packageName, int userId, String conversationId,
|
||||
String statusId) {
|
||||
handleIncomingUser(userId);
|
||||
checkCallerIsSameApp(packageName);
|
||||
mDataManager.clearStatus(packageName, userId, conversationId, statusId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearStatuses(String packageName, int userId, String conversationId) {
|
||||
handleIncomingUser(userId);
|
||||
checkCallerIsSameApp(packageName);
|
||||
mDataManager.clearStatuses(packageName, userId, conversationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParceledListSlice<ConversationStatus> getStatuses(String packageName, int userId,
|
||||
String conversationId) {
|
||||
handleIncomingUser(userId);
|
||||
if (!isSystemOrRoot()) {
|
||||
checkCallerIsSameApp(packageName);
|
||||
}
|
||||
return new ParceledListSlice<>(
|
||||
mDataManager.getStatuses(packageName, userId, conversationId));
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.people.data;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.content.LocusId;
|
||||
import android.content.LocusIdProto;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
@@ -39,6 +40,10 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -101,6 +106,8 @@ public class ConversationInfo {
|
||||
@ConversationFlags
|
||||
private int mConversationFlags;
|
||||
|
||||
private Map<String, ConversationStatus> mCurrStatuses;
|
||||
|
||||
private ConversationInfo(Builder builder) {
|
||||
mShortcutId = builder.mShortcutId;
|
||||
mLocusId = builder.mLocusId;
|
||||
@@ -111,6 +118,7 @@ public class ConversationInfo {
|
||||
mLastEventTimestamp = builder.mLastEventTimestamp;
|
||||
mShortcutFlags = builder.mShortcutFlags;
|
||||
mConversationFlags = builder.mConversationFlags;
|
||||
mCurrStatuses = builder.mCurrStatuses;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -213,6 +221,10 @@ public class ConversationInfo {
|
||||
return hasConversationFlags(FLAG_CONTACT_STARRED);
|
||||
}
|
||||
|
||||
public Collection<ConversationStatus> getStatuses() {
|
||||
return mCurrStatuses.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
@@ -230,14 +242,15 @@ public class ConversationInfo {
|
||||
&& Objects.equals(mParentNotificationChannelId, other.mParentNotificationChannelId)
|
||||
&& Objects.equals(mLastEventTimestamp, other.mLastEventTimestamp)
|
||||
&& mShortcutFlags == other.mShortcutFlags
|
||||
&& mConversationFlags == other.mConversationFlags;
|
||||
&& mConversationFlags == other.mConversationFlags
|
||||
&& Objects.equals(mCurrStatuses, other.mCurrStatuses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber,
|
||||
mNotificationChannelId, mParentNotificationChannelId, mLastEventTimestamp,
|
||||
mShortcutFlags, mConversationFlags);
|
||||
mShortcutFlags, mConversationFlags, mCurrStatuses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,6 +264,7 @@ public class ConversationInfo {
|
||||
sb.append(", notificationChannelId=").append(mNotificationChannelId);
|
||||
sb.append(", parentNotificationChannelId=").append(mParentNotificationChannelId);
|
||||
sb.append(", lastEventTimestamp=").append(mLastEventTimestamp);
|
||||
sb.append(", statuses=").append(mCurrStatuses);
|
||||
sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags));
|
||||
sb.append(" [");
|
||||
if (isShortcutLongLived()) {
|
||||
@@ -321,6 +335,7 @@ public class ConversationInfo {
|
||||
protoOutputStream.write(ConversationInfoProto.CONTACT_PHONE_NUMBER,
|
||||
mContactPhoneNumber);
|
||||
}
|
||||
// ConversationStatus is a transient object and not persisted
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -337,6 +352,7 @@ public class ConversationInfo {
|
||||
out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
|
||||
out.writeUTF(mParentNotificationChannelId != null ? mParentNotificationChannelId : "");
|
||||
out.writeLong(mLastEventTimestamp);
|
||||
// ConversationStatus is a transient object and not persisted
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Failed to write fields to backup payload.", e);
|
||||
return null;
|
||||
@@ -469,6 +485,8 @@ public class ConversationInfo {
|
||||
@ConversationFlags
|
||||
private int mConversationFlags;
|
||||
|
||||
private Map<String, ConversationStatus> mCurrStatuses = new HashMap<>();
|
||||
|
||||
Builder() {
|
||||
}
|
||||
|
||||
@@ -486,6 +504,7 @@ public class ConversationInfo {
|
||||
mLastEventTimestamp = conversationInfo.mLastEventTimestamp;
|
||||
mShortcutFlags = conversationInfo.mShortcutFlags;
|
||||
mConversationFlags = conversationInfo.mConversationFlags;
|
||||
mCurrStatuses = conversationInfo.mCurrStatuses;
|
||||
}
|
||||
|
||||
Builder setShortcutId(@NonNull String shortcutId) {
|
||||
@@ -579,6 +598,26 @@ public class ConversationInfo {
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setStatuses(List<ConversationStatus> statuses) {
|
||||
mCurrStatuses.clear();
|
||||
if (statuses != null) {
|
||||
for (ConversationStatus status : statuses) {
|
||||
mCurrStatuses.put(status.getId(), status);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder addOrUpdateStatus(ConversationStatus status) {
|
||||
mCurrStatuses.put(status.getId(), status);
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder clearStatus(String statusId) {
|
||||
mCurrStatuses.remove(statusId);
|
||||
return this;
|
||||
}
|
||||
|
||||
ConversationInfo build() {
|
||||
Objects.requireNonNull(mShortcutId);
|
||||
return new ConversationInfo(this);
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Person;
|
||||
import android.app.people.ConversationChannel;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.usage.UsageEvents;
|
||||
@@ -75,6 +76,7 @@ import com.android.server.notification.NotificationManagerInternal;
|
||||
import com.android.server.notification.ShortcutHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
@@ -308,6 +310,73 @@ public class DataManager {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public void addOrUpdateStatus(String packageName, int userId, String conversationId,
|
||||
ConversationStatus status) {
|
||||
ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
|
||||
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
|
||||
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
|
||||
builder.addOrUpdateStatus(status);
|
||||
cs.addOrUpdate(builder.build());
|
||||
}
|
||||
|
||||
public void clearStatus(String packageName, int userId, String conversationId,
|
||||
String statusId) {
|
||||
ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
|
||||
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
|
||||
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
|
||||
builder.clearStatus(statusId);
|
||||
cs.addOrUpdate(builder.build());
|
||||
}
|
||||
|
||||
public void clearStatuses(String packageName, int userId, String conversationId) {
|
||||
ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
|
||||
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
|
||||
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
|
||||
builder.setStatuses(null);
|
||||
cs.addOrUpdate(builder.build());
|
||||
}
|
||||
|
||||
public @NonNull List<ConversationStatus> getStatuses(String packageName, int userId,
|
||||
String conversationId) {
|
||||
ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
|
||||
ConversationInfo conversationInfo = getConversationInfoOrThrow(cs, conversationId);
|
||||
Collection<ConversationStatus> statuses = conversationInfo.getStatuses();
|
||||
if (statuses != null) {
|
||||
final ArrayList<ConversationStatus> list = new ArrayList<>(statuses.size());
|
||||
list.addAll(statuses);
|
||||
return list;
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a conversation store for a package, if it exists.
|
||||
*/
|
||||
private @NonNull ConversationStore getConversationStoreOrThrow(String packageName, int userId) {
|
||||
final PackageData packageData = getPackage(packageName, userId);
|
||||
if (packageData == null) {
|
||||
throw new IllegalArgumentException("No settings exist for package " + packageName);
|
||||
}
|
||||
ConversationStore cs = packageData.getConversationStore();
|
||||
if (cs == null) {
|
||||
throw new IllegalArgumentException("No conversations exist for package " + packageName);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a conversation store for a package, if it exists.
|
||||
*/
|
||||
private @NonNull ConversationInfo getConversationInfoOrThrow(ConversationStore cs,
|
||||
String conversationId) {
|
||||
ConversationInfo ci = cs.getConversation(conversationId);
|
||||
|
||||
if (ci == null) {
|
||||
throw new IllegalArgumentException("Conversation does not exist");
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
/** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
|
||||
public void reportShareTargetEvent(@NonNull AppTargetEvent event,
|
||||
@NonNull IntentFilter intentFilter) {
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
|
||||
package com.android.server.people.data;
|
||||
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.content.LocusId;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.net.Uri;
|
||||
@@ -41,6 +47,9 @@ public final class ConversationInfoTest {
|
||||
|
||||
@Test
|
||||
public void testBuild() {
|
||||
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
|
||||
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
|
||||
|
||||
ConversationInfo conversationInfo = new ConversationInfo.Builder()
|
||||
.setShortcutId(SHORTCUT_ID)
|
||||
.setLocusId(LOCUS_ID)
|
||||
@@ -58,6 +67,8 @@ public final class ConversationInfoTest {
|
||||
.setPersonImportant(true)
|
||||
.setPersonBot(true)
|
||||
.setContactStarred(true)
|
||||
.addOrUpdateStatus(cs)
|
||||
.addOrUpdateStatus(cs2)
|
||||
.build();
|
||||
|
||||
assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId());
|
||||
@@ -77,6 +88,8 @@ public final class ConversationInfoTest {
|
||||
assertTrue(conversationInfo.isPersonImportant());
|
||||
assertTrue(conversationInfo.isPersonBot());
|
||||
assertTrue(conversationInfo.isContactStarred());
|
||||
assertThat(conversationInfo.getStatuses()).contains(cs);
|
||||
assertThat(conversationInfo.getStatuses()).contains(cs2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -101,10 +114,15 @@ public final class ConversationInfoTest {
|
||||
assertFalse(conversationInfo.isPersonImportant());
|
||||
assertFalse(conversationInfo.isPersonBot());
|
||||
assertFalse(conversationInfo.isContactStarred());
|
||||
assertThat(conversationInfo.getStatuses()).isNotNull();
|
||||
assertThat(conversationInfo.getStatuses()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildFromAnotherConversationInfo() {
|
||||
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
|
||||
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
|
||||
|
||||
ConversationInfo source = new ConversationInfo.Builder()
|
||||
.setShortcutId(SHORTCUT_ID)
|
||||
.setLocusId(LOCUS_ID)
|
||||
@@ -120,6 +138,8 @@ public final class ConversationInfoTest {
|
||||
.setPersonImportant(true)
|
||||
.setPersonBot(true)
|
||||
.setContactStarred(true)
|
||||
.addOrUpdateStatus(cs)
|
||||
.addOrUpdateStatus(cs2)
|
||||
.build();
|
||||
|
||||
ConversationInfo destination = new ConversationInfo.Builder(source)
|
||||
@@ -141,5 +161,7 @@ public final class ConversationInfoTest {
|
||||
assertTrue(destination.isPersonImportant());
|
||||
assertTrue(destination.isPersonBot());
|
||||
assertFalse(destination.isContactStarred());
|
||||
assertThat(destination.getStatuses()).contains(cs);
|
||||
assertThat(destination.getStatuses()).contains(cs2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.people.data;
|
||||
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
|
||||
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
|
||||
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
|
||||
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
|
||||
@@ -24,6 +26,8 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.Assert.fail;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -50,6 +54,7 @@ import android.app.NotificationManager;
|
||||
import android.app.Person;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.people.ConversationChannel;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
@@ -936,6 +941,83 @@ public final class DataManagerTest {
|
||||
TEST_SHORTCUT_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOrUpdateStatus_noCachedShortcut() {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
|
||||
|
||||
try {
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
|
||||
fail("Updated a conversation info that didn't previously exist");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOrUpdateStatus() {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
|
||||
buildPerson());
|
||||
mDataManager.addOrUpdateConversationInfo(shortcut);
|
||||
|
||||
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
|
||||
|
||||
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
|
||||
.contains(cs);
|
||||
|
||||
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
|
||||
|
||||
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
|
||||
.contains(cs);
|
||||
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
|
||||
.contains(cs2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearStatus() {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
|
||||
buildPerson());
|
||||
mDataManager.addOrUpdateConversationInfo(shortcut);
|
||||
|
||||
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
|
||||
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
|
||||
|
||||
mDataManager.clearStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2.getId());
|
||||
|
||||
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
|
||||
.contains(cs);
|
||||
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
|
||||
.doesNotContain(cs2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearStatuses() {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
|
||||
buildPerson());
|
||||
mDataManager.addOrUpdateConversationInfo(shortcut);
|
||||
|
||||
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
|
||||
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
|
||||
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
|
||||
|
||||
mDataManager.clearStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
|
||||
|
||||
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonCachedShortcutNotInRecentList() {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
Reference in New Issue
Block a user