Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 472a3b33 authored by Shane Brennan's avatar Shane Brennan
Browse files

Add support for audio-focused notifications.

Add extras keys and methos to Notification and RemoteInput for notifications
whose contents have an audio representation and whose RemoteInputs
can accept data results (useful for, e.g., voice messaging
applications, photo results, etc).

This motivator for this change is it enables an end-to-end flow for
audio messaging including
  * System receives the audio message
  * Replying to the message with the user's voice
  * App receives the audio reply data

Note that this CL just adds the capabilities, but does not
add any UI or implementation to perform the above flow.

Test: As agreed, this will be tested both with a set of unit tests,
      and with an "end-to-end" test that simulates two apps, one
      creating an audio message with audio reply action, and a
      second app that executes that action, providing an audio
      result.

Change-Id: I55779ac4b781d8273c13c69fded0b56ee639cf01
parent 094119df
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -5025,6 +5025,7 @@ package android.app {
    field public static final int DEFAULT_LIGHTS = 4; // 0x4
    field public static final int DEFAULT_SOUND = 1; // 0x1
    field public static final int DEFAULT_VIBRATE = 2; // 0x2
    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
    field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -5108,6 +5109,7 @@ package android.app {
    method public android.app.Notification.Action clone();
    method public int describeContents();
    method public boolean getAllowGeneratedReplies();
    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
    method public android.os.Bundle getExtras();
    method public android.graphics.drawable.Icon getIcon();
    method public android.app.RemoteInput[] getRemoteInputs();
@@ -5573,14 +5575,18 @@ package android.app {
  }
  public final class RemoteInput implements android.os.Parcelable {
    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
    method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
    method public int describeContents();
    method public boolean getAllowFreeFormInput();
    method public java.util.Set<java.lang.String> getAllowedDataTypes();
    method public java.lang.CharSequence[] getChoices();
    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
    method public android.os.Bundle getExtras();
    method public java.lang.CharSequence getLabel();
    method public java.lang.String getResultKey();
    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
    method public boolean isDataOnly();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5592,6 +5598,7 @@ package android.app {
    method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
    method public android.app.RemoteInput build();
    method public android.os.Bundle getExtras();
    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
    method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
    method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
    method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+7 −0
Original line number Diff line number Diff line
@@ -5183,6 +5183,7 @@ package android.app {
    field public static final int DEFAULT_LIGHTS = 4; // 0x4
    field public static final int DEFAULT_SOUND = 1; // 0x1
    field public static final int DEFAULT_VIBRATE = 2; // 0x2
    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
    field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -5268,6 +5269,7 @@ package android.app {
    method public android.app.Notification.Action clone();
    method public int describeContents();
    method public boolean getAllowGeneratedReplies();
    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
    method public android.os.Bundle getExtras();
    method public android.graphics.drawable.Icon getIcon();
    method public android.app.RemoteInput[] getRemoteInputs();
@@ -5758,14 +5760,18 @@ package android.app {
  }
  public final class RemoteInput implements android.os.Parcelable {
    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
    method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
    method public int describeContents();
    method public boolean getAllowFreeFormInput();
    method public java.util.Set<java.lang.String> getAllowedDataTypes();
    method public java.lang.CharSequence[] getChoices();
    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
    method public android.os.Bundle getExtras();
    method public java.lang.CharSequence getLabel();
    method public java.lang.String getResultKey();
    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
    method public boolean isDataOnly();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5777,6 +5783,7 @@ package android.app {
    method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
    method public android.app.RemoteInput build();
    method public android.os.Bundle getExtras();
    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
    method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
    method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
    method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+7 −0
Original line number Diff line number Diff line
@@ -5035,6 +5035,7 @@ package android.app {
    field public static final int DEFAULT_LIGHTS = 4; // 0x4
    field public static final int DEFAULT_SOUND = 1; // 0x1
    field public static final int DEFAULT_VIBRATE = 2; // 0x2
    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
    field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
    field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
    field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -5118,6 +5119,7 @@ package android.app {
    method public android.app.Notification.Action clone();
    method public int describeContents();
    method public boolean getAllowGeneratedReplies();
    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
    method public android.os.Bundle getExtras();
    method public android.graphics.drawable.Icon getIcon();
    method public android.app.RemoteInput[] getRemoteInputs();
@@ -5584,14 +5586,18 @@ package android.app {
  }
  public final class RemoteInput implements android.os.Parcelable {
    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
    method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
    method public int describeContents();
    method public boolean getAllowFreeFormInput();
    method public java.util.Set<java.lang.String> getAllowedDataTypes();
    method public java.lang.CharSequence[] getChoices();
    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
    method public android.os.Bundle getExtras();
    method public java.lang.CharSequence getLabel();
    method public java.lang.String getResultKey();
    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
    method public boolean isDataOnly();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5603,6 +5609,7 @@ package android.app {
    method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
    method public android.app.RemoteInput build();
    method public android.os.Bundle getExtras();
    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
    method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
    method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
    method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+83 −4
Original line number Diff line number Diff line
@@ -988,6 +988,32 @@ public class Notification implements Parcelable
     */
    public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";

    /**
     * {@link #extras} key: the audio contents of this notification.
     *
     * This is for use when rendering the notification on an audio-focused interface;
     * the audio contents are a complete sound sample that contains the contents/body of the
     * notification. This may be used in substitute of a Text-to-Speech reading of the
     * notification. For example if the notification represents a voice message this should point
     * to the audio of that message.
     *
     * The data stored under this key should be a String representation of a Uri that contains the
     * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
     *
     * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
     * has a field for holding data URI. That field can be used for audio.
     * See {@code Message#setData}.
     *
     * Example usage:
     * <pre>
     * {@code
     * Notification.Builder myBuilder = (build your Notification as normal);
     * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
     * }
     * </pre>
     */
    public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";

    /** @hide */
    @SystemApi
    public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
@@ -1007,6 +1033,21 @@ public class Notification implements Parcelable
     * to attach actions.
     */
    public static class Action implements Parcelable {
        /**
         * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
         * {@link RemoteInput}s.
         *
         * This is intended for {@link RemoteInput}s that only accept data, meaning
         * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
         * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
         * empty. These {@link RemoteInput}s will be ignored by devices that do not
         * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
         *
         * You can test if a RemoteInput matches these constraints using
         * {@link RemoteInput#isDataOnly}.
         */
        private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";

        private final Bundle mExtras;
        private Icon mIcon;
        private final RemoteInput[] mRemoteInputs;
@@ -1097,12 +1138,27 @@ public class Notification implements Parcelable

        /**
         * Get the list of inputs to be collected from the user when this action is sent.
         * May return null if no remote inputs were added.
         * May return null if no remote inputs were added. Only returns inputs which accept
         * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
         */
        public RemoteInput[] getRemoteInputs() {
            return mRemoteInputs;
        }

        /**
         * Get the list of inputs to be collected from the user that ONLY accept data when this
         * action is sent. These remote inputs are guaranteed to return true on a call to
         * {@link RemoteInput#isDataOnly}.
         *
         * May return null if no data-only remote inputs were added.
         *
         * This method exists so that legacy RemoteInput collectors that pre-date the addition
         * of non-textual RemoteInputs do not access these remote inputs.
         */
        public RemoteInput[] getDataOnlyRemoteInputs() {
            return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
        }

        /**
         * Builder class for {@link Action} objects.
         */
@@ -1226,9 +1282,32 @@ public class Notification implements Parcelable
             * @return the built action
             */
            public Action build() {
                RemoteInput[] remoteInputs = mRemoteInputs != null
                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
                ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
                RemoteInput[] previousDataInputs =
                    (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
                if (previousDataInputs == null) {
                    for (RemoteInput input : previousDataInputs) {
                        dataOnlyInputs.add(input);
                    }
                }
                List<RemoteInput> textInputs = new ArrayList<>();
                if (mRemoteInputs != null) {
                    for (RemoteInput input : mRemoteInputs) {
                        if (input.isDataOnly()) {
                            dataOnlyInputs.add(input);
                        } else {
                            textInputs.add(input);
                        }
                    }
                }
                if (!dataOnlyInputs.isEmpty()) {
                    RemoteInput[] dataInputsArr =
                            dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
                    mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
                }
                RemoteInput[] textInputsArr = textInputs.isEmpty()
                        ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
                return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
                        mAllowGeneratedReplies);
            }
        }
+167 −26

File changed.

Preview size limit exceeded, changes collapsed.