diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/people/IConversationListener.aidl | 33 | ||||
| -rw-r--r-- | core/java/android/app/people/IPeopleManager.aidl | 3 | ||||
| -rw-r--r-- | core/java/android/app/people/PeopleManager.java | 139 |
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)); + } + } } |
