I have an activity with a TabLayout that's hooked up to a FragmentPagerAdapter. Whenever the user changes tabs, another fragment is loaded, but the Activity stays the same.
The activity also contains a search function, i.e. autocomplete suggestions are fetched from a web server. I want to perform a different search action - basically, query another URL - depending on which fragment is currently loaded in the FragmentPager/TabLayout.
So far, I haven't found any way to do this. Here's what I have tried:
currently, the search function is connected to a content provider (via manifest and searchable.xml). However, I haven't found a way to pass data to this ContentProvider to let it know which tab is selected
using multiple content providers doesn't seem to be possible because there can only be one for any given Activity, and the Activity isn't supposed to change when selecting another tab
manipulating the Activity's onQueryText* methods isn't feasible because I don't want to block the UI thread fetching the data
Here's a quick sketch of the situation: Sketch
Here are the relevant code fragments:
MainActivity.class
public class MainActivity extends AppCompatActivity {
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setAdapter(new MainFragmentPagerAdapter(getSupportFragmentManager(),
MainActivity.this));
TabLayout tabs = (TabLayout) findViewById(R.id.sliding_tabs);
tabs.setupWithViewPager(mViewPager);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.action_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setOnQueryTextListener(this);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, MainActivity.class)));
searchView.setIconifiedByDefault(false);
return true;
}
/* ... */
}
menu/action_search.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/action_search"
android:title="Search"
android:icon="#android:drawable/ic_menu_search"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>
xml/searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="hint"
android:label="#string/app_name"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:searchSuggestAuthority="xyz.demo.search.datasuggestion"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestIntentData="content://xyz.demo.search.data"/>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xyz.demo.app">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data
android:name="android.app.default_searchable"
android:value=".MainActivity" />
<activity android:name=".MainActivity" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SEARCH" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
<provider
android:name=".SuggestionProvider"
android:authorities="xyz.demo.search.datasuggestion"
android:enabled="true"
android:exported="true"/>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
SuggestionProvider.java
public class SuggestionProvider extends ContentProvider {
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(".../suggestions")
.build();
try {
Response response = client.newCall(request).execute();
String jsonString = response.body().string();
JSONArray jsonArray = new JSONArray(jsonString);
/* ... */
} catch (Exception e) {
e.printStackTrace();
}
/* ... eventually returns a MatrixCursor */
}
/* ... */
}
Ah okay I see this is one of those things where Android is calling your provider and you can't control how it's going to call it.
I don't see a way to do this cleanly either. The searchable is linked to the activity. You either base your screens on the activity not the fragment (changing your design), or do something terrible like set a preference when your fragment resumes so the content provider knows which fragment is "active".
Related
During changes from Holo to Material using Google support library I found an issue that it seems like onSearchRequested callback of Activity is not called. Both Activities - the one that starts search (ProjectDetail) and the one that is search target (ResultList) - are extending android.support.v7.app.ActionBarActivity.
Search works but it never reaches onSearchRequested callback therefore I cannot set additional parameters to search bundle.
There are many similar questions but none of the solutions worked for me so far, therefore I am asking here. I am fighting this for a couple of hours and maybe I am missing something terribly obvious as desperation is obscuring my vision :) Pretty please, help!
Snippet form Manifest:
<application>
<meta-data
android:name="android.app.default_searchable"
android:value=".ResultList" />
<activity android:name="my.package.ProjectDetail">
<meta-data
android:name="android.app.default_searchable"
android:value=".ResultList"/>
</activity>
<activity android:name="my.package.ResultList" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.actions.SEARCH_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="#xml/searchable" />
</activity>
</application>
This is in onCreateOptionsMenu in Activity that starts the search:
getMenuInflater().inflate(R.menu.search, menu);
MenuItem searchItem = menu.findItem(R.id.menu_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchableInfo info = searchManager.getSearchableInfo(getComponentName());
searchView.setSearchableInfo(info);
searchView.setIconifiedByDefault(true);
searchView.setQueryRefinementEnabled(true);
searchView.setQueryHint(getString(R.string.search_hint));
This is search.xml inflated in previous snippet, containing MenuItem and ActionView:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/menu_search"
android:title="#string/m_search"
android:icon="#android:drawable/ic_menu_search"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>
And this is onSearchRequested callback in ProjectDetail Activity:
#Override
public boolean onSearchRequested() {
L.dd("SEARCH", "REQUESTED IN PROJECT");
Bundle searchData = new Bundle();
searchData.putLong(Const.EXTRA_PROJECT_ID, projectId);
searchData.putLong(Const.EXTRA_ACCOUNT_ID, accountId);
startSearch(null, false, searchData, false);
return true;
}
Finally, this is searchable.xml as referenced from Manifest:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_label"
android:hint="#string/search_hint"
android:searchSuggestAuthority="my.package.provider.SearchSuggestionsProvider"
android:searchSuggestSelection=" ?"
android:includeInGlobalSearch="true"
/>
Thanks in advance.
I had the same problem. Found one simple workaround here.
Generally speaking you override method startActivity in search activity and put params to intent when action is equal to Intent.ACTION_SEARCH:
#Override
public void startActivity(Intent intent) {
// check if search intent
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
intent.putExtra("KEY", "VALUE");
}
super.startActivity(intent);
}
And then in searchable activity get passed params like this:
private void handleIntent(Intent intent){
if (Intent.ACTION_SEARCH.equals(intent.getAction())){
mSearchedQuery = intent.getStringExtra(SearchManager.QUERY);
mExtraData = intent.getStringExtra("KEY");
}
I'm trying to implement searching in my android app. There are two activities, the MainActivity where search is launched (via searchview in action bar) and ResultsActivity, where search is handled and a list is inflated with results. The problem is that the getSearchableInfo method that i invoke in onCreateOptionsMenu in the main activity returns a null, like no searchable.xml were attached to the activity. I'v tried many combinations of meta data arrangement in the manifest, also every solution found on the internet and other stackoverflow questions, but nothing seems to work.
These are the sources:
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/Theme.AppCompat.Light" >
<activity
android:name="com.example.webparser.MainActivity"
android:label="#string/app_name">
<intent-filter>
<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>
<activity android:name="com.example.ResultsActivity" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
</activity>
<meta-data
android:name="android.app.default_searchable"
android:value=".ResultsActivity" />
</application>
** MainActivity.java **
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
SearchManager searchManager = (SearchManager) getSystemService(getApplicationContext().SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search)
.getActionView();
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
Log.d("Search:", searchManager.getSearchableInfo(getComponentName())
.toString());
return true;
}
** ResultsActivity.java **
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
elementi = new ArrayList<String>();
for(Ricetta ricetta:ricette){
if( ricetta.getNome().toLowerCase().matches(query) ){
elementi.add(ricetta.getNome());
}
}
adapter=new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
elementi);
lista.setAdapter(adapter);
Log.i("LOG:", "searching");
}
}
** searchable.xml **
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name"
/>
Please notice that I inserted a Log.d where the searchview is supposed to be initialized, and where is called getSearchableInfo, so at runtime a nullpointerexception is raised and the app doesn't launch, but if that Log.d is removed then everything but the search works (on pressing enter in the search box nothing happens). I think there is a problem with the manifest, but I can't figure out what it is. Does anyone have any clue?
I also tried to launch the main activity in singleTop pointing the searchable meta data tags to the main activity, but the same exception is raised.
I notice that you are using Menu to try an obtain your search view instead of the findViewById function
SearchView searchView = (SearchView) menu.findItem(R.id.search)
.getActionView()
Should be more like this
SearchView searchView = (SearchView) findViewById(R.id.search);
This would give you the SearchView you are looking for.
Faced the same problem. Following helped me:
This is my MainActivity
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_list_view_test, menu);
SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
MenuItem searchItem = menu.findItem(R.id.search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setSearchableInfo( searchManager.getSearchableInfo(getComponentName()) );
return true;
Note: Make sure that you have imported SearchView from Support.v7.widget
(import android.support.v7.widget.SearchView;)
This is my Menu.xml for MainActivity
< item android:id="#+id/search"
android:title="Search"
android:icon="#drawable/ic_action_search"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
In my case I have similar set up just a little bit different. I have:
<meta-data android:name="android.app.default_searchable"
android:value=".ResultsActivity" />
in my Main Activity. And
<meta-data android:name="android.app.searchable"
android:resource="#xml/searchable" />
and
searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName));
should work.
in my ResultActivity.
And I create a component name in my MainActivity with:
ComponentName componentName = new ComponentName(context, ResultActivity.class);
Note: be careful with your item layout:
Use: app:actionViewClass="android.support.v7.widget.SearchView" if you have problem with android:actionViewClass="android.support.v7.widget.SearchView"
I'm using the SearchView widget in ActionbarSherlock as a follows:
File.java
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.main, menu);
SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.MenuSearch).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(true);
searchView.setSubmitButtonEnabled(true);
return true;
}
File.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/MenuSearch" android:title="#string/Bs"
android:icon="#drawable/ic_action_search"
android:showAsAction="always|withText"
android:actionViewClass="com.actionbarsherlock.widget.SearchView" >
</item>
</menu>
In my application I get to show the search icon and select it unfolds the search box for the widget, but when I write any search I do not know how to interact with the widget SearchView to launch a new activity and show a series of results.
With the command searchView.setSubmitButtonEnabled(true); appears a icon similar to 'play' and I suppose it is for just that, but I do not know how to interact with it.
Can anyone help?
Official android documentation here describes how start new activity to handle search query and display results.
First you need to create (if you havent already) searchable configuration in res/xml/searchable.xml, like this:
<?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" />
Set this configuration for your activity, that contains SearchView in actionbar, like this (in manifest):
<activity ... >
...
<meta-data android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
So basically, you need your searchable activity (new activity that will start after submitting search query) to handle the android.intent.action.SEARCH action. Declare it in manifest like this:
<activity android:name=".SearchResultsActivity" ... >
...
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
...
</activity>
And your searchable activity should look like this:
public class SearchResultsActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
...
handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
...
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//use the query to search your data somehow
}
}
...
}
I try to make SearchView in action bar to start a SearchResultsActivity which is registered to handle ACTION_SEARCH. I do all required steps but still didn't work!
Here's the code:
1. Search View menu item: (menu/main.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/search_action"
android:icon="#android:drawable/ic_menu_search"
android:actionViewClass="android.widget.SearchView"
android:showAsAction="always|collapseActionView"
/>
</menu>
2. Searchable configuration (xml/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"/>
2.1 adding Searchable configuration link to MainActivity in AndroidManifest.xml:
<activity
android:name="com.me.searchonpre3.MainActivity"
android:label="#string/app_name">
<intent-filter>
<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>
3. Bind the Search View menu item with the search configuration (MainActivity.java):
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
SearchView searchView = (SearchView) menu.findItem(R.id.search_action).getActionView();
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (searchView != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
}
}
return true;
}
4. add new Activity "SearchResultsActivity" and mark it to handle SEARCH_ACTIONs:
public class SearchResultsActivity extends ListActivity {
public static final String TAG = SearchResultsActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "In SearchResult OnCreate");
setContentView(R.layout.activity_search_results);
this.setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
new String[]{"Product1", "Product2"}));
}
}
AndroidMainfest.xml:
<activity
android:name="com.me.searchonpre3.SearchResultsActivity"
android:label="#string/title_activity_search_results">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
</activity>
All the above settings and the SearchResultsActivity is not started when press the Search keyboard icon.
What else Should I do???
Note: When I add a setOnQueryTextListener, the callbacks being called on the MainActivity class.
It seems that Android tutorials are misleading.
The answer is here: https://stackoverflow.com/questions/11699206#11704661
In my application i made my content provider searchable, defining my main activity as the default search activity on my manifest.
<activity
android:name="com.pt.application.MainActivity"
android:label="#string/title_activity_map"
android:launchMode="singleTop"
android:theme="#style/AppTheme" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
Then i implemented the onSuggestionClick option to update a map, based on the suggestion clicked by the user. The searchview is on the action bar.
The problem is that when the user clicks on a suggestion, somehow the activity is recreated (i think OnPause and OnResume are called?). Is there any way to prevent this from happening? I don't want that my activity is "recreated" every time the user clicks on a suggestion.
If more code is needed, please ask. Thanks.
EDIT: Code added
<?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:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:voiceLanguageModel="web_search"
android:searchSuggestAuthority="com.pt.provider"
android:searchSuggestIntentData="content://com.pt.provider/busstop">
</searchable>
On my main activity:
// Bind the Activity’s SearchableInfo to the Search View
searchView = (SearchView)menu.findItem(R.id.menu_search).getActionView();
this.setupSearchView();
private void setupSearchView()
{
// Use the Search Manager to find the SearchableInfo related
// to this Activity.
SearchManager searchManager =
(SearchManager)getSystemService(Context.SEARCH_SERVICE);
SearchableInfo searchableInfo =
searchManager.getSearchableInfo(getComponentName());
searchView.setSearchableInfo(searchableInfo);
searchView.setSubmitButtonEnabled(false);
searchView.setOnSuggestionListener(new OnSuggestionListener() {
#Override
public boolean onSuggestionSelect(int position) {
return false;
}
#Override
public boolean onSuggestionClick(int position) {
// Do things
return false;
}
});
}
Solved my question. Changed on the manifest:
From:
android:launchMode="singleTop"
To:
android:launchMode="singleTask"