I have a AutoCompleteTextView which gives user auto-completion search result from Google Places API. Once I was done I discovered SearchView and how it can be placed in the ActionBar. I checked out the SearchView example provided by google and added it to my application as a starting point (it lists the installed applications) but I don´t know how to proceed from here. I want to have the same functionality as AutoCompleteTextView but use the SearchView instead. Any suggestion? The whole class is provided below.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.SearchManager; import android.app.SearchableInfo; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.ImageView; import android.widget.ListView; import android.widget.SearchView; import android.widget.TextView; import android.widget.Toast; /** * * @author * */ public class PlacesListSearchActivity extends Activity implements SearchView.OnQueryTextListener{ private static final String TAG = "PlacesListActivity"; private ResultReceiver mReceiver; private OnSharedPreferenceChangeListener sharedPreferencesListener; private SharedPreferences sharedPreferences; /** Called when the activity is first created. */ public ArrayAdapter<String> adapter; public AutoCompleteTextView textView; private SearchView mSearchView; private TextView mStatusView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_ACTION_BAR); setContentView(R.layout.places_search); mStatusView = (TextView) findViewById(R.id.status_text); final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.item_list); textView = (AutoCompleteTextView)findViewById(R.id.autoCompleteTextView1); adapter.setNotifyOnChange(true); textView.setHint("type store name"); textView.setAdapter(adapter); textView.addTextChangedListener(new TextWatcher() { public void onTextChanged(CharSequence s, int start, int before, int count) { if (count%3 == 1) { adapter.clear(); GetPlaces task = new GetPlaces(); //now pass the argument in the textview to the task task.execute(textView.getText().toString()); } } public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } public void afterTextChanged(Editable s) { } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.searchview_in_menu, menu); MenuItem searchItem = menu.findItem(R.id.action_search); mSearchView = (SearchView) searchItem.getActionView(); setupSearchView(searchItem); return true; } private void setupSearchView(MenuItem searchItem) { if (isAlwaysExpanded()) { mSearchView.setIconifiedByDefault(false); } else { searchItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); } SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); if (searchManager != null) { List<SearchableInfo> searchables = searchManager.getSearchablesInGlobalSearch(); // Try to use the "applications" global search provider SearchableInfo info = searchManager.getSearchableInfo(getComponentName()); for (SearchableInfo inf : searchables) { if (inf.getSuggestAuthority() != null && inf.getSuggestAuthority().startsWith("applications")) { info = inf; } } mSearchView.setSearchableInfo(info); } mSearchView.setOnQueryTextListener(this); } public boolean onQueryTextChange(String newText) { mStatusView.setText("Query = " + newText); return false; } public boolean onQueryTextSubmit(String query) { mStatusView.setText("Query = " + query + " : submitted"); return false; } public boolean onClose() { mStatusView.setText("Closed!"); return false; } protected boolean isAlwaysExpanded() { return false; } class GetPlaces extends AsyncTask<String, Void, ArrayList<String>> { @Override // three dots is java for an array of strings protected ArrayList<String> doInBackground(String... args) { Log.d("PlacesListActivity", "doInBackground"); ArrayList<String> predictionsArr = new ArrayList<String>(); try { URL googlePlaces = new URL( "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=" + URLEncoder.encode(args[0], "UTF-8") + // "&types=geocode&language=en&sensor=true&key=" + SEARCHES FOR GEO CODES "&types=establishment&language=en&sensor=true&key=" + getResources().getString(R.string.googleAPIKey)); Log.d("PlacesListActivity", googlePlaces.toString()); URLConnection tc = googlePlaces.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader( tc.getInputStream())); String line; StringBuffer sb = new StringBuffer(); //take Google's legible JSON and turn it into one big string. while ((line = in.readLine()) != null) { sb.append(line); } //turn that string into a JSON object JSONObject predictions = new JSONObject(sb.toString()); //now get the JSON array that's inside that object JSONArray ja = new JSONArray(predictions.getString("predictions")); for (int i = 0; i < ja.length(); i++) { JSONObject jo = (JSONObject) ja.get(i); //add each entry to our array predictionsArr.add(jo.getString("description")); } } catch (IOException e) { Log.e("PlacesListActivity", "GetPlaces : doInBackground", e); } catch (JSONException e) { Log.e("PlacesListActivity", "GetPlaces : doInBackground", e); } return predictionsArr; } @Override protected void onPostExecute(ArrayList<String> result){ Log.d("PlacesListActivity", "onPostExecute : " + result.size()); //update the adapter adapter = new ArrayAdapter<String>(getBaseContext(), R.layout.item_list); adapter.setNotifyOnChange(true); //attach the adapter to textview textView.setAdapter(adapter); for (String string : result) { Log.d("PlacesListActivity", "onPostExecute : result = " + string); adapter.add(string); adapter.notifyDataSetChanged(); } Log.d("PlacesListActivity", "onPostExecute : autoCompleteAdapter" + adapter.getCount()); } } }
After updating the code suggested by saxman, I can see that the query method in the provider is never called:
My manifest file looks like this:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.rathinavelu.rea" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".PlacesListActivity" > </activity> <activity android:name=".PlacesListSearchActivity" > <action android:name="android.intent.action.SEARCH" /> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity> <activity android:name=".TestMapActivity" > </activity> <activity android:name=".SettingsPreferencesActivity" > </activity> <activity android:name="com.rathinavelu.util.ConnectionChecker" > </activity> <uses-library android:name="com.google.android.maps" /> <service android:name=".places.PlacesRESTService" android:enabled="true" android:exported="false" > <intent-filter> <action android:name="android.intent.action.ACTION_SYNC" /> </intent-filter> </service> <provider android:name=".places.PlacesSuggestionProvider" android:authorities="com.rathinavelu.rea.places.search_suggestion_provider" android:syncable="false" /> </application> </manifest>
I use the same authority in the manifest, content provider and manifest file. I see the searchView in the menu and I have not modified the query method so It should just return the one line cursor. but the query method is never called. please help. Another issue I just spotted is that the searchView does not show the specified search_hint!
Providing more code *PlacesListSearchActivity.java*
public class PlacesListSearchActivity extends Activity { private static final String TAG = "PlacesListSearchActivity"; private ResultReceiver mReceiver; private OnSharedPreferenceChangeListener sharedPreferencesListener; private SharedPreferences sharedPreferences; /** Called when the activity is first created. */ public ArrayAdapter<String> adapter; public AutoCompleteTextView textView; private SearchView mSearchView; private TextView mStatusView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_ACTION_BAR); setContentView(R.layout.places_search); mStatusView = (TextView) findViewById(R.id.status_text); final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.item_list); textView = (AutoCompleteTextView)findViewById(R.id.autoCompleteTextView1); adapter.setNotifyOnChange(true); textView.setHint("type store name"); textView.setAdapter(adapter); textView.addTextChangedListener(new TextWatcher() { public void onTextChanged(CharSequence s, int start, int before, int count) { if (count%3 == 1) { adapter.clear(); GetPlaces task = new GetPlaces(); //now pass the argument in the textview to the task task.execute(textView.getText().toString()); } } public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } public void afterTextChanged(Editable s) { } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // super.onCreateOptionsMenu(menu); // Inflate the options menu from XML MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.places_list_search_options_menu, menu); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); // Tells your app's SearchView to use this activity's searchable configuration searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default // setupSearchView(searchItem); return true; }
places_search.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#dddddd"> <AutoCompleteTextView android:id="@+id/autoCompleteTextView1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" > <requestFocus></requestFocus> </AutoCompleteTextView> <TextView android:id="@+id/status_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal"/> </RelativeLayout>
places_list_search_options_menu.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/menu_search" android:title="@string/menu_search" android:icon="@android:drawable/ic_menu_search" android:showAsAction="collapseActionView|ifRoom" android:actionViewClass="android.widget.SearchView" /> </menu>
searchable.xml
<?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/app_name" android:hint="@string/search_hint" android:searchSuggestAuthority="com.rathinavelu.rea.places.search_suggestion_provider"> </searchable>
PlacesSuggestionProvider.java
public class PlacesSuggestionProvider extends ContentProvider { private static final String LOG_TAG = "PlacesSuggestionProvider"; public static final String AUTHORITY = "com.rathinavelu.rea.places.search_suggestion_provider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search"); // UriMatcher constant for search suggestions private static final int SEARCH_SUGGEST = 1; private static final UriMatcher uriMatcher; private static final String[] SEARCH_SUGGEST_COLUMNS = { BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_2, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID }; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST); uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST); } @Override public int delete(Uri uri, String arg1, String[] arg2) { throw new UnsupportedOperationException(); } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case SEARCH_SUGGEST: return SearchManager.SUGGEST_MIME_TYPE; default: throw new IllegalArgumentException("Unknown URL " + uri); } } @Override public Uri insert(Uri uri, ContentValues arg1) { throw new UnsupportedOperationException(); } @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.d(LOG_TAG, "query = " + uri); // Use the UriMatcher to see what kind of query we have switch (uriMatcher.match(uri)) { case SEARCH_SUGGEST: Log.d(LOG_TAG, "Search suggestions requested."); MatrixCursor cursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 1); cursor.addRow(new String[] { "1", "Search Result", "Search Result Description", "content_id" }); return cursor; default: throw new IllegalArgumentException("Unknown Uri: " + uri); } } @Override public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3) { throw new UnsupportedOperationException(); } }
SearchView widget can be implemented over ToolBar/ActionBar or inside a layout. SearchView is by default collapsible and set to be iconified using setIconifiedByDefault(true) method of SearchView class. For making search field visible, SearchView uses setIconifiedByDefault(false) method.
android.widget.SearchView. A widget that provides a user interface for the user to enter a search query and submit a request to a search provider. Shows a list of query suggestions or results, if available, and allows the user to pick a suggestion or result to launch into.
To get Places Autocomplete API results in a SearchView, you'll first need a ContentProvider for the API.
import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.provider.BaseColumns; import android.util.Log; public class PlacesSuggestionProvider extends ContentProvider { private static final String LOG_TAG = "ExampleApp"; public static final String AUTHORITY = "com.example.google.places.search_suggestion_provider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search"); // UriMatcher constant for search suggestions private static final int SEARCH_SUGGEST = 1; private static final UriMatcher uriMatcher; private static final String[] SEARCH_SUGGEST_COLUMNS = { BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_2, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID }; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST); uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST); } @Override public int delete(Uri uri, String arg1, String[] arg2) { throw new UnsupportedOperationException(); } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case SEARCH_SUGGEST: return SearchManager.SUGGEST_MIME_TYPE; default: throw new IllegalArgumentException("Unknown URL " + uri); } } @Override public Uri insert(Uri uri, ContentValues arg1) { throw new UnsupportedOperationException(); } @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.d(LOG_TAG, "query = " + uri); // Use the UriMatcher to see what kind of query we have switch (uriMatcher.match(uri)) { case SEARCH_SUGGEST: Log.d(LOG_TAG, "Search suggestions requested."); MatrixCursor cursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 1); cursor.addRow(new String[] { "1", "Search Result", "Search Result Description", "content_id" }); return cursor; default: throw new IllegalArgumentException("Unknown Uri: " + uri); } } @Override public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3) { throw new UnsupportedOperationException(); } }
Then add your Places Autocomplete API client code into the query method on the content provider. You extract the user input as follows:
String query = uri.getLastPathSegment().toLowerCase();
Add the PlacesSuggestionProvider to your AndroidManifest, and make sure your activity has a searchable configuration.
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".PlacesSearchViewActivity" > <intent-filter> <action android:name="android.intent.action.SEARCH" /> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity> <provider android:name="com.example.google.places.PlacesSuggestionProvider" android:authorities="com.example.google.places.search_suggestion_provider" android:syncable="false" /> </application> </manifest>
And make sure your searchable configuration (res/xml/searchable.xml) has a search suggest authority.
<?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/app_name" android:hint="@string/search_hint" android:searchSuggestAuthority="com.example.google.places.search_suggestion_provider"> </searchable>
The authority should be the same in AndroidManifest.xml, searchable.xml, and your content provider.
Create a options menu for your ActionBar that includes a SearchView (/res/menu/options_menu.xml).
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_search" android:title="@string/menu_search" android:icon="@drawable/ic_menu_search" android:showAsAction="collapseActionView|ifRoom" android:actionViewClass="android.widget.SearchView" /> </menu>
Configure your Activity with a SearchView that's associated with your searchable configuration/
@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the options menu from XML MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); // Tells your app's SearchView to use this activity's searchable configuration searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default return true; }
A few key docs are:
Adding Custom Suggestions: http://developer.android.com/guide/topics/search/adding-custom-suggestions.html
Creating a Content Provider: http://developer.android.com/guide/topics/providers/content-provider-creating.html
Using a Search Widget: http://developer.android.com/guide/topics/search/search-dialog.html#UsingSearchWidget
AutoCompleteTextView with google search Api
your xml
<AutoCompleteTextView android:id="@+id/main_omnibox_input" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:background="@null" android:hint="Search" android:focusable="true" android:focusableInTouchMode="true" android:selectAllOnFocus="true" android:singleLine="true" android:textSize="@dimen/_14sdp" />
your java code
this.inputBox = (AutoCompleteTextView) findViewById(R.id.main_omnibox_input); inputBox.setAdapter(new SearchAutocompleteAdapter(SearchActivity.this, new SearchAutocompleteAdapter.OnSearchCommitListener() { @Override public void onSearchCommit(String text) { inputBox.setText(text); inputBox.setSelection(text.length()); } })); this.inputBox.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) { String charSequence = ((TextView) view.findViewById(android.R.id.text1)).getText().toString(); inputBox.setText(Html.fromHtml(BrowserUnit.urlWrapper(charSequence)), BufferType.SPANNABLE); inputBox.setSelection(charSequence.length()); // your code // updateAlbum(charSequence); // hideSoftInput(SearchActivity.this.inputBox); } });
SearchAutocompleteAdapter
public class SearchAutocompleteAdapter extends BaseAdapter implements Filterable { interface OnSearchCommitListener { void onSearchCommit(String text); } private final Context mContext; private final OnSearchCommitListener commitListener; private List<String> completions = new ArrayList<>(); static final String searchCompleteUrl = "https://www.google.com/complete/search?client=firefox&q=%s"; SearchAutocompleteAdapter(Context context, OnSearchCommitListener commitListener) { mContext = context; this.commitListener = commitListener; } @Override public int getCount() { return completions.size(); } @Override public Object getItem(int position) { return completions.get(position); } @Override public long getItemId(int position) { return position; } @SuppressLint("ClickableViewAccessibility") @Override @SuppressWarnings("ConstantConditions") public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false); } TextView textview = convertView.findViewById(android.R.id.text1); textview.setText(completions.get(position)); Drawable d = ContextCompat.getDrawable(mContext, R.drawable.icon_goarrowsmall); final int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, mContext.getResources().getDisplayMetrics()); d.setBounds(0, 0, size, size); textview.setCompoundDrawables(null, null, d, null); textview.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { if (event.getAction() != MotionEvent.ACTION_DOWN) { return false; } TextView t = (TextView) view; if (event.getX() > t.getWidth() - t.getCompoundPaddingRight()) { commitListener.onSearchCommit(getItem(position).toString()); return true; } return false; } }); parent.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { if (event.getX() > view.getWidth() - size * 2) { return true; } return false; } }); return convertView; } @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { // Invoked on a worker thread FilterResults filterResults = new FilterResults(); if (constraint != null) { List<String> results = getCompletions(constraint.toString()); filterResults.values = results; filterResults.count = results.size(); } return filterResults; } @Override @SuppressWarnings("unchecked") protected void publishResults(CharSequence constraint, FilterResults results) { if (results != null && results.count > 0) { completions = (List<String>) results.values; notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } }; } private List<String> getCompletions(String text) { int total = 0; byte[] data = new byte[16384]; try { URL url = new URL(URLUtil.composeSearchUrl(text, searchCompleteUrl, "%s")); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); try { InputStream in = new BufferedInputStream(urlConnection.getInputStream()); while (total <= data.length) { int count = in.read(data, total, data.length - total); if (count == -1) { break; } total += count; } if (total == data.length) { // overflow return new ArrayList<>(); } } finally { urlConnection.disconnect(); } } catch (IOException e) { return new ArrayList<>(); } JSONArray jsonArray; try { jsonArray = new JSONArray(new String(data, StandardCharsets.UTF_8)); } catch (JSONException e) { return new ArrayList<>(); } jsonArray = jsonArray.optJSONArray(1); if (jsonArray == null) { return new ArrayList<>(); } final int MAX_RESULTS = 10; List<String> result = new ArrayList<>(Math.min(jsonArray.length(), MAX_RESULTS)); for (int i = 0; i < jsonArray.length() && result.size() < MAX_RESULTS; i++) { String s = jsonArray.optString(i); if (s != null && !s.isEmpty()) { result.add(s); } } return result; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With