summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/people/IConversationListener.aidl33
-rw-r--r--core/java/android/app/people/IPeopleManager.aidl3
-rw-r--r--core/java/android/app/people/PeopleManager.java139
3 files changed, 166 insertions, 9 deletions
diff --git a/core/java/android/app/people/IConversationListener.aidl b/core/java/android/app/people/IConversationListener.aidl
new file mode 100644
index 000000000000..7cbd66dd6617
--- /dev/null
+++ b/core/java/android/app/people/IConversationListener.aidl
@@ -0,0 +1,33 @@
+/**
+ * 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.app.people.ConversationChannel;
+import android.content.pm.ParceledListSlice;
+import android.os.UserHandle;
+
+import java.util.List;
+
+/**
+ * Interface for PeopleManager#ConversationListener.
+ *
+ * @hide
+ */
+oneway interface IConversationListener
+{
+ void onConversationUpdate(in ConversationChannel conversation);
+} \ No newline at end of file
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index d000f3b0b01d..496ca82bb61f 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -18,6 +18,7 @@ package android.app.people;
import android.app.people.ConversationStatus;
import android.app.people.ConversationChannel;
+import android.app.people.IConversationListener;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
import android.os.IBinder;
@@ -62,4 +63,6 @@ interface IPeopleManager {
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);
+ void registerConversationListener(in String packageName, int userId, in String shortcutId, in IConversationListener callback);
+ void unregisterConversationListener(in IConversationListener callback);
}
diff --git a/core/java/android/app/people/PeopleManager.java b/core/java/android/app/people/PeopleManager.java
index d348edb6dfbf..108437eb0649 100644
--- a/core/java/android/app/people/PeopleManager.java
+++ b/core/java/android/app/people/PeopleManager.java
@@ -16,6 +16,8 @@
package android.app.people;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -25,12 +27,18 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.Pair;
+import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* This class allows interaction with conversation and people data.
@@ -40,11 +48,18 @@ public final class PeopleManager {
private static final String LOG_TAG = PeopleManager.class.getSimpleName();
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public Map<ConversationListener, Pair<Executor, IConversationListener>>
+ mConversationListeners = new HashMap<>();
+
@NonNull
- private final Context mContext;
+ private Context mContext;
@NonNull
- private final IPeopleManager mService;
+ private IPeopleManager mService;
/**
* @hide
@@ -56,6 +71,15 @@ public final class PeopleManager {
}
/**
+ * @hide
+ */
+ @VisibleForTesting
+ public PeopleManager(@NonNull Context context, IPeopleManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
* Returns whether a shortcut has a conversation associated.
*
* <p>Requires android.permission.READ_PEOPLE_DATA permission.
@@ -66,9 +90,8 @@ public final class PeopleManager {
* clients.
*
* @param packageName name of the package the conversation is part of
- * @param shortcutId the shortcut id backing the conversation
+ * @param shortcutId the shortcut id backing the conversation
* @return whether the {@shortcutId} is backed by a Conversation.
- *
* @hide
*/
@SystemApi
@@ -94,8 +117,7 @@ public final class PeopleManager {
*
* @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
- *
+ * @param status the current status for the given conversation
* @return whether the role is available in the system
*/
public void addOrUpdateStatus(@NonNull String conversationId,
@@ -115,8 +137,8 @@ public final class PeopleManager {
*
* @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
+ * @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);
@@ -155,7 +177,7 @@ public final class PeopleManager {
try {
final ParceledListSlice<ConversationStatus> parceledList
= mService.getStatuses(
- mContext.getPackageName(), mContext.getUserId(), conversationId);
+ mContext.getPackageName(), mContext.getUserId(), conversationId);
if (parceledList != null) {
return parceledList.getList();
}
@@ -164,4 +186,103 @@ public final class PeopleManager {
}
return new ArrayList<>();
}
+
+ /**
+ * Listeners for conversation changes.
+ *
+ * @hide
+ */
+ public interface ConversationListener {
+ /**
+ * Triggers when the conversation registered for a listener has been updated.
+ *
+ * @param conversation The conversation with modified data
+ * @see IPeopleManager#registerConversationListener(String, int, String,
+ * android.app.people.ConversationListener)
+ *
+ * <p>Only system root and SysUI have access to register the listener.
+ */
+ default void onConversationUpdate(@NonNull ConversationChannel conversation) {
+ }
+ }
+
+ /**
+ * Register a listener to watch for changes to the conversation identified by {@code
+ * packageName}, {@code userId}, and {@code shortcutId}.
+ *
+ * @param packageName The package name to match and filter the conversation to send updates for.
+ * @param userId The user ID to match and filter the conversation to send updates for.
+ * @param shortcutId The shortcut ID to match and filter the conversation to send updates for.
+ * @param listener The listener to register to receive conversation updates.
+ * @param executor {@link Executor} to handle the listeners. To dispatch listeners to the
+ * main thread of your application, you can use
+ * {@link android.content.Context#getMainExecutor()}.
+ * @hide
+ */
+ public void registerConversationListener(String packageName, int userId, String shortcutId,
+ ConversationListener listener, Executor executor) {
+ requireNonNull(listener, "Listener cannot be null");
+ requireNonNull(packageName, "Package name cannot be null");
+ requireNonNull(shortcutId, "Shortcut ID cannot be null");
+ synchronized (mConversationListeners) {
+ IConversationListener proxy = (IConversationListener) new ConversationListenerProxy(
+ executor, listener);
+ try {
+ mService.registerConversationListener(
+ packageName, userId, shortcutId, proxy);
+ mConversationListeners.put(listener,
+ new Pair<>(executor, proxy));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters the listener previously registered to watch conversation changes.
+ *
+ * @param listener The listener to register to receive conversation updates.
+ * @hide
+ */
+ public void unregisterConversationListener(
+ ConversationListener listener) {
+ requireNonNull(listener, "Listener cannot be null");
+
+ synchronized (mConversationListeners) {
+ if (mConversationListeners.containsKey(listener)) {
+ IConversationListener proxy = mConversationListeners.remove(listener).second;
+ try {
+ mService.unregisterConversationListener(proxy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Listener proxy class for {@link ConversationListener}
+ *
+ * @hide
+ */
+ private static class ConversationListenerProxy extends
+ IConversationListener.Stub {
+ private final Executor mExecutor;
+ private final ConversationListener mListener;
+
+ ConversationListenerProxy(Executor executor, ConversationListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onConversationUpdate(@NonNull ConversationChannel conversation) {
+ if (mListener == null || mExecutor == null) {
+ // Binder is dead.
+ Slog.e(LOG_TAG, "Binder is dead");
+ return;
+ }
+ mExecutor.execute(() -> mListener.onConversationUpdate(conversation));
+ }
+ }
}