1ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen/* 2ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Copyright (C) 2014 The Android Open Source Project 3ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 4ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Licensed under the Apache License, Version 2.0 (the "License"); 5ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * you may not use this file except in compliance with the License. 6ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * You may obtain a copy of the License at 7ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 8ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * http://www.apache.org/licenses/LICENSE-2.0 9ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 10ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Unless required by applicable law or agreed to in writing, software 11ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * distributed under the License is distributed on an "AS IS" BASIS, 12ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * See the License for the specific language governing permissions and 14ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * limitations under the License. 15ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */ 16ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 17ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenpackage android.support.v4.app; 18ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 19ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.app.RemoteInput; 2059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennanimport android.content.ClipData; 2159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennanimport android.content.ClipDescription; 22ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.Intent; 2359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennanimport android.net.Uri; 24ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.Bundle; 258f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi; 26ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 2759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennanimport java.util.HashMap; 2859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennanimport java.util.Map; 2959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan 308f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette@RequiresApi(20) 31ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenclass RemoteInputCompatApi20 { 3259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan /** Extra added to a clip data intent object to hold the data results bundle. */ 3359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan private static final String EXTRA_DATA_TYPE_RESULTS_DATA = 3459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan "android.remoteinput.dataTypeResultsData"; 3559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan 36ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen static RemoteInputCompatBase.RemoteInput[] toCompat(RemoteInput[] srcArray, 37ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen RemoteInputCompatBase.RemoteInput.Factory factory) { 38ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen if (srcArray == null) { 39ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return null; 40ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 41ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen RemoteInputCompatBase.RemoteInput[] result = factory.newArray(srcArray.length); 42ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen for (int i = 0; i < srcArray.length; i++) { 43ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen RemoteInput src = srcArray[i]; 44ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen result[i] = factory.build(src.getResultKey(), src.getLabel(), src.getChoices(), 4559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan src.getAllowFreeFormInput(), src.getExtras(), null); 46ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 47ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return result; 48ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 49ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 50ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen static RemoteInput[] fromCompat(RemoteInputCompatBase.RemoteInput[] srcArray) { 51ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen if (srcArray == null) { 52ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return null; 53ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 54ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen RemoteInput[] result = new RemoteInput[srcArray.length]; 55ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen for (int i = 0; i < srcArray.length; i++) { 56ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen RemoteInputCompatBase.RemoteInput src = srcArray[i]; 57ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen result[i] = new RemoteInput.Builder(src.getResultKey()) 58ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen .setLabel(src.getLabel()) 59ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen .setChoices(src.getChoices()) 60ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen .setAllowFreeFormInput(src.getAllowFreeFormInput()) 612bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen .addExtras(src.getExtras()) 62ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen .build(); 63ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 64ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return result; 65ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 66ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 67ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen static Bundle getResultsFromIntent(Intent intent) { 68ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return RemoteInput.getResultsFromIntent(intent); 69ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 70ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 7159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan static Map<String, Uri> getDataResultsFromIntent(Intent intent, String remoteInputResultKey) { 7259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Intent clipDataIntent = getClipDataIntentFromIntent(intent); 7359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (clipDataIntent == null) { 7459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return null; 7559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 7659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Map<String, Uri> results = new HashMap<>(); 7759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Bundle extras = clipDataIntent.getExtras(); 7859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan for (String key : extras.keySet()) { 7959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (key.startsWith(EXTRA_DATA_TYPE_RESULTS_DATA)) { 8059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan String mimeType = key.substring(EXTRA_DATA_TYPE_RESULTS_DATA.length()); 8159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (mimeType == null || mimeType.isEmpty()) { 8259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan continue; 8359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 8459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Bundle bundle = clipDataIntent.getBundleExtra(key); 8559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan String uriStr = bundle.getString(remoteInputResultKey); 8659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (uriStr == null || uriStr.isEmpty()) { 8759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan continue; 8859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 8959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan results.put(mimeType, Uri.parse(uriStr)); 9059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 9159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 9259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return results.isEmpty() ? null : results; 9359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 9459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan 95ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen static void addResultsToIntent(RemoteInputCompatBase.RemoteInput[] remoteInputs, 96ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen Intent intent, Bundle results) { 9759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan // Implementations of RemoteInput#addResultsToIntent prior to SDK 26 don't actually add 9859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan // results, they wipe out old results and insert the new one. Work around that by preserving 9959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan // old results. 10059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Bundle existingTextResults = getResultsFromIntent(intent); 10159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (existingTextResults == null) { 10259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan existingTextResults = results; 10359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } else { 10459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan existingTextResults.putAll(results); 10559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 10659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan for (RemoteInputCompatBase.RemoteInput input : remoteInputs) { 10759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan // Data results are also wiped out. So grab them and add them back in. 10859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Map<String, Uri> existingDataResults = 10959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan getDataResultsFromIntent(intent, input.getResultKey()); 11059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan RemoteInputCompatBase.RemoteInput[] arr = new RemoteInputCompatBase.RemoteInput[1]; 11159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan arr[0] = input; 11259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan RemoteInput.addResultsToIntent(fromCompat(arr), intent, existingTextResults); 11359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (existingDataResults != null) { 11459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan addDataResultToIntent(input, intent, existingDataResults); 11559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 11659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 11759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 11859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan 11959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan /** 12059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan * Same as {@link #addResultsToIntent} but for setting data results. 12159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan * @param remoteInput The remote input for which results are being provided 12259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan * @param intent The intent to add remote input results to. The {@link ClipData} 12359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan * field of the intent will be modified to contain the results. 12459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan * @param results A map of mime type to the Uri result for that mime type. 12559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan */ 12659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan public static void addDataResultToIntent(RemoteInputCompatBase.RemoteInput remoteInput, 12759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Intent intent, Map<String, Uri> results) { 12859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Intent clipDataIntent = getClipDataIntentFromIntent(intent); 12959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (clipDataIntent == null) { 13059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan clipDataIntent = new Intent(); // First time we've added a result. 13159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 13259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan for (Map.Entry<String, Uri> entry : results.entrySet()) { 13359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan String mimeType = entry.getKey(); 13459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Uri uri = entry.getValue(); 13559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (mimeType == null) { 13659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan continue; 13759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 13859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan Bundle resultsBundle = 13959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan clipDataIntent.getBundleExtra(getExtraResultsKeyForData(mimeType)); 14059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (resultsBundle == null) { 14159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan resultsBundle = new Bundle(); 14259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 14359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan resultsBundle.putString(remoteInput.getResultKey(), uri.toString()); 14459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle); 14559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 14659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan intent.setClipData(ClipData.newIntent(RemoteInput.RESULTS_CLIP_LABEL, clipDataIntent)); 14759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 14859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan 14959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan private static String getExtraResultsKeyForData(String mimeType) { 15059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType; 15159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 15259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan 15359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan private static Intent getClipDataIntentFromIntent(Intent intent) { 15459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan ClipData clipData = intent.getClipData(); 15559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (clipData == null) { 15659d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return null; 15759d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 15859d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan ClipDescription clipDescription = clipData.getDescription(); 15959d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) { 16059d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return null; 16159d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 16259d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan if (!clipDescription.getLabel().equals(RemoteInput.RESULTS_CLIP_LABEL)) { 16359d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return null; 16459d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan } 16559d562bdab983469340a054bd3bc888034fa6bc8Shane Brennan return clipData.getItemAt(0).getIntent(); 166ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 167ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen} 168