1/*
2 * Copyright (C) 2014 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
17package android.support.v4.app;
18
19import android.content.Intent;
20import android.os.Build;
21import android.os.Bundle;
22import android.util.Log;
23
24/**
25 * Helper for using the {@link android.app.RemoteInput} API
26 * introduced after API level 4 in a backwards compatible fashion.
27 */
28public final class RemoteInput extends RemoteInputCompatBase.RemoteInput {
29    private static final String TAG = "RemoteInput";
30
31    /** Label used to denote the clip data type used for remote input transport */
32    public static final String RESULTS_CLIP_LABEL = RemoteInputCompatJellybean.RESULTS_CLIP_LABEL;
33
34    /** Extra added to a clip data intent object to hold the results bundle. */
35    public static final String EXTRA_RESULTS_DATA = RemoteInputCompatJellybean.EXTRA_RESULTS_DATA;
36
37    private final String mResultKey;
38    private final CharSequence mLabel;
39    private final CharSequence[] mChoices;
40    private final boolean mAllowFreeFormInput;
41    private final Bundle mExtras;
42
43    private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
44            boolean allowFreeFormInput, Bundle extras) {
45        this.mResultKey = resultKey;
46        this.mLabel = label;
47        this.mChoices = choices;
48        this.mAllowFreeFormInput = allowFreeFormInput;
49        this.mExtras = extras;
50    }
51
52    /**
53     * Get the key that the result of this input will be set in from the Bundle returned by
54     * {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent.
55     */
56    public String getResultKey() {
57        return mResultKey;
58    }
59
60    /**
61     * Get the label to display to users when collecting this input.
62     */
63    public CharSequence getLabel() {
64        return mLabel;
65    }
66
67    /**
68     * Get possible input choices. This can be {@code null} if there are no choices to present.
69     */
70    public CharSequence[] getChoices() {
71        return mChoices;
72    }
73
74    /**
75     * Get whether or not users can provide an arbitrary value for
76     * input. If you set this to {@code false}, users must select one of the
77     * choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown
78     * if you set this to false and {@link #getChoices} returns {@code null} or empty.
79     */
80    public boolean getAllowFreeFormInput() {
81        return mAllowFreeFormInput;
82    }
83
84    /**
85     * Get additional metadata carried around with this remote input.
86     */
87    public Bundle getExtras() {
88        return mExtras;
89    }
90
91    /**
92     * Builder class for {@link android.support.v4.app.RemoteInput} objects.
93     */
94    public static final class Builder {
95        private final String mResultKey;
96        private CharSequence mLabel;
97        private CharSequence[] mChoices;
98        private boolean mAllowFreeFormInput = true;
99        private Bundle mExtras = new Bundle();
100
101        /**
102         * Create a builder object for {@link android.support.v4.app.RemoteInput} objects.
103         * @param resultKey the Bundle key that refers to this input when collected from the user
104         */
105        public Builder(String resultKey) {
106            if (resultKey == null) {
107                throw new IllegalArgumentException("Result key can't be null");
108            }
109            mResultKey = resultKey;
110        }
111
112        /**
113         * Set a label to be displayed to the user when collecting this input.
114         * @param label The label to show to users when they input a response.
115         * @return this object for method chaining
116         */
117        public Builder setLabel(CharSequence label) {
118            mLabel = label;
119            return this;
120        }
121
122        /**
123         * Specifies choices available to the user to satisfy this input.
124         * @param choices an array of pre-defined choices for users input.
125         *        You must provide a non-null and non-empty array if
126         *        you disabled free form input using {@link #setAllowFreeFormInput}.
127         * @return this object for method chaining
128         */
129        public Builder setChoices(CharSequence[] choices) {
130            mChoices = choices;
131            return this;
132        }
133
134        /**
135         * Specifies whether the user can provide arbitrary values.
136         *
137         * @param allowFreeFormInput The default is {@code true}.
138         *         If you specify {@code false}, you must provide a non-null
139         *         and non-empty array to {@link #setChoices} or an
140         *         {@link IllegalArgumentException} is thrown.
141         * @return this object for method chaining
142         */
143        public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
144            mAllowFreeFormInput = allowFreeFormInput;
145            return this;
146        }
147
148        /**
149         * Merge additional metadata into this builder.
150         *
151         * <p>Values within the Bundle will replace existing extras values in this Builder.
152         *
153         * @see RemoteInput#getExtras
154         */
155        public Builder addExtras(Bundle extras) {
156            if (extras != null) {
157                mExtras.putAll(extras);
158            }
159            return this;
160        }
161
162        /**
163         * Get the metadata Bundle used by this Builder.
164         *
165         * <p>The returned Bundle is shared with this Builder.
166         */
167        public Bundle getExtras() {
168            return mExtras;
169        }
170
171        /**
172         * Combine all of the options that have been set and return a new
173         * {@link android.support.v4.app.RemoteInput} object.
174         */
175        public RemoteInput build() {
176            return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
177        }
178    }
179
180    /**
181     * Get the remote input results bundle from an intent. The returned Bundle will
182     * contain a key/value for every result key populated by remote input collector.
183     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value.
184     * @param intent The intent object that fired in response to an action or content intent
185     *               which also had one or more remote input requested.
186     */
187    public static Bundle getResultsFromIntent(Intent intent) {
188        return IMPL.getResultsFromIntent(intent);
189    }
190
191    /**
192     * Populate an intent object with the results gathered from remote input. This method
193     * should only be called by remote input collection services when sending results to a
194     * pending intent.
195     * @param remoteInputs The remote inputs for which results are being provided
196     * @param intent The intent to add remote inputs to. The {@link android.content.ClipData}
197     *               field of the intent will be modified to contain the results.
198     * @param results A bundle holding the remote input results. This bundle should
199     *                be populated with keys matching the result keys specified in
200     *                {@code remoteInputs} with values being the result per key.
201     */
202    public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
203            Bundle results) {
204        IMPL.addResultsToIntent(remoteInputs, intent, results);
205    }
206
207    private static final Impl IMPL;
208
209    interface Impl {
210        Bundle getResultsFromIntent(Intent intent);
211        void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
212                Bundle results);
213    }
214
215    static class ImplBase implements Impl {
216        @Override
217        public Bundle getResultsFromIntent(Intent intent) {
218            Log.w(TAG, "RemoteInput is only supported from API Level 16");
219            return null;
220        }
221
222        @Override
223        public void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, Bundle results) {
224            Log.w(TAG, "RemoteInput is only supported from API Level 16");
225        }
226    }
227
228    static class ImplJellybean implements Impl {
229        @Override
230        public Bundle getResultsFromIntent(Intent intent) {
231            return RemoteInputCompatJellybean.getResultsFromIntent(intent);
232        }
233
234        @Override
235        public void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, Bundle results) {
236            RemoteInputCompatJellybean.addResultsToIntent(remoteInputs, intent, results);
237        }
238    }
239
240    static class ImplApi20 implements Impl {
241        @Override
242        public Bundle getResultsFromIntent(Intent intent) {
243            return RemoteInputCompatApi20.getResultsFromIntent(intent);
244        }
245
246        @Override
247        public void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, Bundle results) {
248            RemoteInputCompatApi20.addResultsToIntent(remoteInputs, intent, results);
249        }
250    }
251
252    static {
253        if (Build.VERSION.SDK_INT >= 20) {
254            IMPL = new ImplApi20();
255        } else if (Build.VERSION.SDK_INT >= 16) {
256            IMPL = new ImplJellybean();
257        } else {
258            IMPL = new ImplBase();
259        }
260    }
261
262    /** @hide */
263    public static final Factory FACTORY = new Factory() {
264        @Override
265        public RemoteInput build(String resultKey,
266                CharSequence label, CharSequence[] choices, boolean allowFreeFormInput,
267                Bundle extras) {
268            return new RemoteInput(resultKey, label, choices, allowFreeFormInput, extras);
269        }
270
271        @Override
272        public RemoteInput[] newArray(int size) {
273            return new RemoteInput[size];
274        }
275    };
276}
277