diff --git a/docs-android/app/app.iml b/docs-android/app/app.iml index 3aac58b8..7821ca38 100644 --- a/docs-android/app/app.iml +++ b/docs-android/app/app.iml @@ -102,6 +102,7 @@ + diff --git a/docs-android/app/build.gradle b/docs-android/app/build.gradle index c48d012a..7a40c4ba 100644 --- a/docs-android/app/build.gradle +++ b/docs-android/app/build.gradle @@ -35,4 +35,5 @@ dependencies { compile 'com.android.support:recyclerview-v7:21.0.0' compile 'com.loopj.android:android-async-http:1.4.6' compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5' + compile 'de.greenrobot:eventbus:2.4.0' } diff --git a/docs-android/app/src/main/AndroidManifest.xml b/docs-android/app/src/main/AndroidManifest.xml index 2f0116b7..6719ebd1 100644 --- a/docs-android/app/src/main/AndroidManifest.xml +++ b/docs-android/app/src/main/AndroidManifest.xml @@ -27,12 +27,19 @@ android:label="@string/app_name" android:logo="@drawable/ic_launcher" android:launchMode="singleTop"> + + + + + diff --git a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java index 9e354e37..88924c12 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java +++ b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java @@ -23,7 +23,6 @@ public class MainApplication extends Application { // TODO Fullscreen preview // TODO Sharing // TODO Error feedback - // TODO Searching // TODO Printing super.onCreate(); diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java index 5a91da98..ade0539f 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java @@ -1,18 +1,28 @@ package com.sismics.docs.activity; +import android.app.SearchManager; +import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; +import android.provider.SearchRecentSuggestions; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; +import android.widget.TextView; import com.sismics.docs.R; +import com.sismics.docs.event.SearchEvent; import com.sismics.docs.model.application.ApplicationContext; +import com.sismics.docs.provider.RecentSuggestionsProvider; + +import org.json.JSONObject; + +import de.greenrobot.event.EventBus; /** * Main activity. @@ -21,8 +31,8 @@ import com.sismics.docs.model.application.ApplicationContext; */ public class MainActivity extends ActionBarActivity { - private ListView drawerList; private ActionBarDrawerToggle drawerToggle; + private MenuItem searchItem; @Override protected void onCreate(final Bundle args) { @@ -38,30 +48,27 @@ public class MainActivity extends ActionBarActivity { // Setup the activity setContentView(R.layout.main_activity); - // Cache view references + // Enable ActionBar app icon to behave as action to toggle nav drawer DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - drawerList = (ListView) findViewById(R.id.drawer_list); - - // Drawer item click listener - drawerList.setOnItemClickListener(new ListView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - } - }); - - if (drawerLayout != null) { - // Enable ActionBar app icon to behave as action to toggle nav drawer - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - } - - // ActionBarDrawerToggle ties together the the proper interactions - // between the sliding drawer and the action bar app icon - drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, - R.string.drawer_open, R.string.drawer_close); - drawerLayout.setDrawerListener(drawerToggle); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); } + + // ActionBarDrawerToggle ties together the the proper interactions + // between the sliding drawer and the action bar app icon + drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, + R.string.drawer_open, R.string.drawer_close); + drawerLayout.setDrawerListener(drawerToggle); + + // Fill the drawer user info + JSONObject userInfo = ApplicationContext.getInstance().getUserInfo(); + TextView usernameTextView = (TextView) findViewById(R.id.usernameTextView); + usernameTextView.setText(userInfo.optString("username")); + TextView emailTextView = (TextView) findViewById(R.id.emailTextView); + emailTextView.setText(userInfo.optString("email")); + + handleIntent(getIntent()); } @Override @@ -70,7 +77,7 @@ public class MainActivity extends ActionBarActivity { case android.R.id.home: // The action bar home/up action should open or close the drawer. // ActionBarDrawerToggle will take care of this. - if (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) { + if (drawerToggle.onOptionsItemSelected(item)) { return true; } return true; @@ -83,24 +90,69 @@ public class MainActivity extends ActionBarActivity { @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - if (drawerToggle != null) { - // Sync the toggle state after onRestoreInstanceState has occurred. - drawerToggle.syncState(); - } + // Sync the toggle state after onRestoreInstanceState has occurred. + drawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (drawerToggle != null) { - // Pass any configuration change to the drawer toggle - drawerToggle.onConfigurationChanged(newConfig); - } + // Pass any configuration change to the drawer toggle + drawerToggle.onConfigurationChanged(newConfig); } + @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt("drawerItemSelected", drawerList.getCheckedItemPosition()); + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main_activity, menu); + + // Get the SearchView and set the searchable configuration + SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); + searchItem = menu.findItem(R.id.action_search); + SearchView searchView = (SearchView) searchItem.getActionView(); + searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); + + searchView.setOnCloseListener(new SearchView.OnCloseListener() { + @Override + public boolean onClose() { + EventBus.getDefault().post(new SearchEvent(null)); + return false; + } + }); + + return super.onCreateOptionsMenu(menu); + } + + @Override + protected void onNewIntent(Intent intent) { + setIntent(intent); + handleIntent(intent); + } + + /** + * Handle the incoming intent. + * + * @param intent Intent + */ + private void handleIntent(Intent intent) { + // Intent is consumed + setIntent(null); + + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + // Perform a search query + String query = intent.getStringExtra(SearchManager.QUERY); + + // Collapse the SearchView + if (searchItem != null) { + searchItem.collapseActionView(); + } + + // Save the query + SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, RecentSuggestionsProvider.AUTHORITY, RecentSuggestionsProvider.MODE); + suggestions.saveRecentQuery(query, null); + + EventBus.getDefault().post(new SearchEvent(query)); + } } } \ No newline at end of file diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/DocListAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/DocListAdapter.java index cc4aa19c..9da23c57 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/adapter/DocListAdapter.java +++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/DocListAdapter.java @@ -88,14 +88,21 @@ public class DocListAdapter extends RecyclerView.Adapter previousTotal) { @@ -80,26 +87,54 @@ public class DocListFragment extends Fragment { } } if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + 3) { - loadDocuments(); + loadDocuments(false); loading = true; } } }); // Grab the documents - loadDocuments(); + loadDocuments(true); + EventBus.getDefault().register(this); return view; } + @Override + public void onDestroyView() { + EventBus.getDefault().unregister(this); + super.onDestroyView(); + } + + /** + * A search event has been fired. + * + * @param event Search event + */ + public void onEvent(SearchEvent event) { + query = event.getQuery(); + loadDocuments(true); + } + /** * Refresh the document list. + * + * @param reset If true, reload the documents */ - private void loadDocuments() { - DocumentResource.list(getActivity(), adapter.getItemCount(), new JsonHttpResponseHandler() { + private void loadDocuments(final boolean reset) { + if (reset) { + loading = true; + previousTotal = 0; + adapter.clearDocuments(); + if (getView() != null) { + getView().findViewById(R.id.progressBar).setVisibility(View.VISIBLE); + } + } + + DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - adapter.addDocuments(response.optJSONArray("documents"), false); + adapter.addDocuments(response.optJSONArray("documents")); if (getView() != null) { getView().findViewById(R.id.progressBar).setVisibility(View.GONE); } diff --git a/docs-android/app/src/main/java/com/sismics/docs/provider/RecentSuggestionsProvider.java b/docs-android/app/src/main/java/com/sismics/docs/provider/RecentSuggestionsProvider.java new file mode 100644 index 00000000..94f4839a --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/provider/RecentSuggestionsProvider.java @@ -0,0 +1,17 @@ +package com.sismics.docs.provider; + +import android.content.SearchRecentSuggestionsProvider; + +/** + * Search recent suggestions provider. + * + * @author bgamard. + */ +public class RecentSuggestionsProvider extends SearchRecentSuggestionsProvider { + public final static String AUTHORITY = "com.sismics.docs.provider.RecentSuggestionsProvider"; + public final static int MODE = DATABASE_MODE_QUERIES; + + public RecentSuggestionsProvider() { + setupSuggestions(AUTHORITY, MODE); + } +} diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java index a8d575db..c9121882 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java @@ -15,9 +15,11 @@ public class DocumentResource extends BaseResource { * GET /document/list. * * @param context Context + * @param offset Offset + * @param query Search query * @param responseHandler Callback */ - public static void list(Context context, int offset, JsonHttpResponseHandler responseHandler) { + public static void list(Context context, int offset, String query, JsonHttpResponseHandler responseHandler) { init(context); RequestParams params = new RequestParams(); @@ -25,6 +27,7 @@ public class DocumentResource extends BaseResource { params.put("offset", offset); params.put("sort_column", 3); params.put("asc", false); + params.put("search", query); client.get(getApiUrl(context) + "/document/list", params, responseHandler); } } diff --git a/docs-android/app/src/main/res/drawable-xhdpi/ic_launcher.png b/docs-android/app/src/main/res/drawable-xhdpi/ic_launcher.png index 71c6d760..1d2a2d4f 100644 Binary files a/docs-android/app/src/main/res/drawable-xhdpi/ic_launcher.png and b/docs-android/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/docs-android/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/docs-android/app/src/main/res/drawable-xxhdpi/ic_launcher.png index 4df18946..a4de9a1a 100644 Binary files a/docs-android/app/src/main/res/drawable-xxhdpi/ic_launcher.png and b/docs-android/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/docs-android/app/src/main/res/layout/main_activity.xml b/docs-android/app/src/main/res/layout/main_activity.xml index c38cd8af..d8fc692b 100644 --- a/docs-android/app/src/main/res/layout/main_activity.xml +++ b/docs-android/app/src/main/res/layout/main_activity.xml @@ -18,12 +18,40 @@ android:layout_height="match_parent" android:layout_gravity="start" android:gravity="center" - android:orientation="vertical"> + android:orientation="vertical" + android:background="#fff" + android:elevation="5dp"> + + + + + + + + diff --git a/docs-android/app/src/main/res/menu/main_activity.xml b/docs-android/app/src/main/res/menu/main_activity.xml new file mode 100644 index 00000000..a635ec73 --- /dev/null +++ b/docs-android/app/src/main/res/menu/main_activity.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/docs-android/app/src/main/res/values/strings.xml b/docs-android/app/src/main/res/values/strings.xml index 8551a5d0..87ff6bd2 100644 --- a/docs-android/app/src/main/res/values/strings.xml +++ b/docs-android/app/src/main/res/values/strings.xml @@ -30,5 +30,6 @@ Downloading file number %1s Download all files Downloading document + Search documents diff --git a/docs-android/app/src/main/res/xml/searchable.xml b/docs-android/app/src/main/res/xml/searchable.xml new file mode 100644 index 00000000..4635951c --- /dev/null +++ b/docs-android/app/src/main/res/xml/searchable.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file