Android app that shows results from several search providers.
Check out the server repo for more information.
App is available on Google Play Store, but you are welcome to download the source code and use it as you please.
The app comes with some default dictionaries (which are called Search Providers). If you wish to add your own search providers or remove the default ones, there are several steps to complete.
We will now demonstrate how to add a new search provider to the app. For this practice, we will add Wikipedia.
Note that there are few superclasses that you will extend in order to add a provider. Those superclasses will take care of most of the app's functionality (e.g. showing "no results" card when no results are available. or showing "server error" card with a retry button when an error occurs). You could override any of the superclass' methods if you want to achieve more complex behavior. In such case, the code is documented and you are advised to go through it.
-
First, make sure that your server (see server repo) supports your search provider (see add search provider)
-
Go to
ApiConsts
class underApiClients
package, and add the following String:public static final String DICTIONARY_WIKIPEDIA = "Wikipedia";
The String holds the display name of the search provider.
And then add that String to the array
SEARCH_PROVIDERS
:public static final String[] SEARCH_PROVIDERS = { DICTIONARY_WIKIPEDIA };
This array defines what search providers are available.
-
Go to
ClientFactory
class underApiClients
package, and add the following code:interface Client { @GET("/dic/wikipedia") Call<ResponseBody> getWikipediaDefinitions(@Query("t") String term); }
This code defines a Retrofit client.
Now, we will add that call to our Client factory:
public static Call<ResponseBody> getCall(Bundle queryBundle, String searchProvider, String url) { switch (searchProvider) { case ApiConsts.DICTIONARY_WIKIPEDIA: return getClient(url).getWikipediaDefinitions(queryBundle.getString(ApiConsts.QUERY_BUNDLE_TERM)); // ...rest of calls and default call... } }
-
Now, we will create Java objects that wrap our json response from the server.
You can use jsonschema2pojo to generate the appropriate java objects.
Place the objects under
SearchProviders
package.This objects extends a base class called
Results
:public class WikipediaResults extends Results { @SerializedName("title") @Expose private String title; @SerializedName("extract") @Expose private String extract; public WikipediaResults(int error, String title, String extract) { super(error); this.title = title; this.extract = extract; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getExtract() { return extract; } public void setExtract(String extract) { this.extract = extract; } @Override public int getCount() { return (getTitle().equals("")) ? 0 : 1; } }
There are 2 important things to notice:
- On the constructor
WikipediaResults
, the parametererror
must be set by the super class constructor:super(error)
. - You must override the method
getCount()
which tells us how many results are available. In our case, there is a single result if the title is not empty.
- On the constructor
-
Now, we will create a layout for our data. This layout will be shown inside the results cards. Under
res/layout
create an xml layout file calleddefinition_wikipedia.xml
:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:tools="http://schemas.android.com/tools" android:padding="25dp" android:orientation="vertical"> <android.support.v7.widget.AppCompatTextView style="@android:style/TextAppearance.Holo.Large" android:id="@+id/textViewWikipediaTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:textStyle="bold" android:textSize="30sp" android:textIsSelectable="false" android:layout_marginEnd="@dimen/dictionary_icon_size" tools:text="title" /> <android.support.v7.widget.AppCompatTextView style="@android:style/TextAppearance.Holo.Large" android:id="@+id/textViewWikipediaExtract" android:layout_width="match_parent" android:layout_height="wrap_content" android:textStyle="bold" android:textSize="20sp" android:textIsSelectable="false" android:layout_marginEnd="@dimen/dictionary_icon_size" tools:text="extract" /> </LinearLayout>
This layout consist of 2
TextView
views which hold the title and the extract (definition) of the search result. It looks like that: -
Now, we will create an adapter under
Adapters
package that is calledWikipediaTermAdapter
. This adapter is a subclass ofTermAdapter
. The superclass will take care of most of the hard work.public class WikipediaTermAdapter extends TermAdapter { public class MyViewHolder extends TermAdapterViewHolder { public TextView title; public TextView extract; public MyViewHolder(View view) { super(view); title = (TextView) view.findViewById(R.id.textViewWikipediaTitle); extract = (TextView) view.findViewById(R.id.textViewWikipediaExtract); } } public WikipediaTermAdapter(Context mContext, int error, ResponseBody responseBody, View.OnClickListener retryOnClickListener) { super(mContext, error, responseBody, retryOnClickListener); } @Override public InnerTermViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { InnerTermViewHolder innerTermViewHolder = super.onCreateViewHolder(parent, viewType); innerTermViewHolder.outerTermAdapter = new MyViewHolder(innerTermViewHolder.view); return innerTermViewHolder; } @Override public void setDefinitionLayout(InnerTermViewHolder oldHolder, int position) { WikipediaResults term = ((WikipediaResults) results); MyViewHolder holder = ((MyViewHolder)oldHolder.outerTermAdapter); holder.title.setText(term.getTitle()); holder.extract.setText(term.getExtract()); } @Override protected Results createResultsObject(ResponseBody responseBody) { if (responseBody != null) { try { return new Gson().fromJson(responseBody.string(), WikipediaResults.class); } catch (IOException ex) { // do nothing } } return null; } @Override protected int getDefinitionLayoutId() { return R.layout.definition_wikipedia; } @Override protected Drawable getIconDrawable() { return mContext.getResources().getDrawable(R.drawable.wikipedia_480); } }
Let's take a look at the important things in here:
- If you've already used RecyclerView before, you may see that the
MyViewHolder
class is a standardViewHolder
. - Create a
WikipediaTermAdapter
constructor that calls its super class constructor. - Override the method
InnerTermViewHolder
as shown. This way, we make sure that the super class gets the correctMyViewHolder
class. - Implement the abstract method
setDefinitionLayout
. This method sets the layout as you wish. Note that we take the method's parameters and downcast them to the classes we have just created. It's your responsibility to perform the correct cast. - Implement the abstract method
createResultsObject
which uses Gson to convert the json response to our java objects. - Implement the abstract method
getDefinitionLayoutId
which returns the appropriate layout id. - If you wish to add an icon to your card result, override the method
getIconDrawable
. If you won't, no icon will be shown.
- If you've already used RecyclerView before, you may see that the
-
Go to
TermAdapterFactory
class underAdapters
package, and add the adapter we have just created to the factory:public static TermAdapter getTermAdapter(String searchProviderName, Context mContext, int error, ResponseBody responseBody, View.OnClickListener retryOnClickListener) { switch (searchProviderName) { case ApiConsts.DICTIONARY_WIKIPEDIA: return new WikipediaTermAdapter(mContext, error, responseBody, retryOnClickListener); } return null; }
And that's it.
To remove search providers go to ApiConsts
class under ApiClients
package, and remove the unwanted providers from the array SEARCH_PROVIDERS
:
public static final String[] SEARCH_PROVIDERS = {
DICTIONARY_IMAGES,
// DICTIONARY_MORIFX, <- Remove this line
// DICTIONARY_MILOG, <- Remove this line
DICTIONARY_WIKIPEDIA,
DICTIONARY_URBAN_DICTIONARY
};
Now only images, wikipedia and urban dictionary are available throughout the app.
Evyatar Ben-Shitrit - eviabs
This project is licensed under the MIT License.