package in.vesely.eclub.yodaqa.view; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.preference.PreferenceManager; import android.speech.RecognizerIntent; import android.speech.tts.TextToSpeech; import android.support.design.widget.TabLayout; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import com.quinny898.library.persistentsearch.SearchBox; import com.quinny898.library.persistentsearch.SearchResult; import com.squareup.otto.Subscribe; import org.androidannotations.annotations.AfterInject; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Background; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.NonConfigurationInstance; import org.androidannotations.annotations.OptionsItem; import org.androidannotations.annotations.OptionsMenu; import org.androidannotations.annotations.Transactional; import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.res.DrawableRes; import org.androidannotations.annotations.rest.RestService; import org.springframework.http.client.OkHttpClientHttpRequestFactory; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Locale; import in.vesely.eclub.yodaqa.R; import in.vesely.eclub.yodaqa.adapters.ResponseFragmentTabAdapter; import in.vesely.eclub.yodaqa.bus.OttoBus; import in.vesely.eclub.yodaqa.bus.RequestUpdateAction; import in.vesely.eclub.yodaqa.bus.ResponseChangedAction; import in.vesely.eclub.yodaqa.db.DBHelper; import in.vesely.eclub.yodaqa.db.SearchItem; import in.vesely.eclub.yodaqa.restclient.YodaAnswersResponse; import in.vesely.eclub.yodaqa.restclient.YodaErrorHandler; import in.vesely.eclub.yodaqa.restclient.YodaExecuter; import in.vesely.eclub.yodaqa.restclient.YodaRestClient; @EActivity(R.layout.activity_main) @OptionsMenu(R.menu.main) public class MainActivity extends AppCompatActivity implements SearchBox.SearchListener, TextToSpeech.OnInitListener { private static final String RESPONSE_STATE = "response_state"; private static final String TAG = "MainActivity"; @ViewById(R.id.tabLayout) protected TabLayout tabLayout; @ViewById(R.id.pager) protected ViewPager pager; @ViewById(R.id.searchbox) protected SearchBox search; @DrawableRes(R.drawable.ic_action_history) protected Drawable historyDrawable; @RestService protected YodaRestClient restClient; @Bean protected YodaErrorHandler errorHandler; @Bean protected OttoBus bus; private final Object lock = new Object(); private TextToSpeech t1; @NonConfigurationInstance protected YodaExecuter executer; private DBHelper dbHelper; private YodaAnswersResponse response; private boolean searchOpened; private static final int MY_DATA_CHECK_CODE = 131; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PreferenceManager.setDefaultValues(this, R.xml.preferences, false); bus.register(this); dbHelper = new DBHelper(this); Intent checkTTSIntent = new Intent(); checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE); setEndpoint(); } @Override public void onResume() { super.onResume(); setEndpoint(); } @Override public void onDestroy() { super.onDestroy(); if (t1 != null) { t1.stop(); t1.shutdown(); t1 = null; } bus.unregister(this); } private void setEndpoint() { SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); String settedEndpoint = sharedPref.getString("endpoint", ""); if (settedEndpoint.equals("Movies")) { restClient.setRootUrl("http://qa.ailao.eu:4000/"); } else { restClient.setRootUrl("http://qa.ailao.eu/"); } } @AfterViews protected void afterViews() { pager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); tabLayout.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(pager)); ResponseFragmentTabAdapter adapter = new ResponseFragmentTabAdapter( getSupportFragmentManager(), getFragments(), new String[]{getString(R.string.answer_title), getString(R.string.sources_title)}); pager.setAdapter(adapter); tabLayout.setTabsFromPagerAdapter(adapter); search.enableVoiceRecognition(this); search.setMenuListener(new SearchBox.MenuListener() { @Override public void onMenuClick() { if (!searchOpened) { Log.d(TAG, "Search bar menu clicked -> opening."); search.toggleSearch(); } } }); search.setSearchListener(this); search.setLogoText(getString(R.string.enter_something)); loadResults(); } @Background protected void loadResults() { SQLiteDatabase db = dbHelper.getReadableDatabase(); ArrayList<SearchResult> results = new ArrayList<>(); for (String s : SearchItem.select(db)) { results.add(new SearchResult(s, historyDrawable)); } search.setInitialResults(results); db.close(); } private List<Class<? extends ResponseFragment>> getFragments() { List<Class<? extends ResponseFragment>> fragments = new LinkedList<>(); fragments.add(AnswersFragment_.class); fragments.add(SourcesFragment_.class); return fragments; } @AfterInject protected void afterInject() { restClient.getRestTemplate().setRequestFactory(new OkHttpClientHttpRequestFactory()); restClient.setRestErrorHandler(errorHandler); } @Subscribe public void onRequestRefresh(RequestUpdateAction action) { } @Subscribe public void onResponseChanged(ResponseChangedAction action) { response = action.getResponse(); if (response != null && response.isFinished()) { if (t1 != null) { t1.speak(response.getTextForSpokenAnswer(), TextToSpeech.QUEUE_FLUSH, null); } else { Log.d(TAG, "TTS is not instantiated."); } } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SearchBox.VOICE_RECOGNITION_CODE && resultCode == RESULT_OK) { ArrayList<String> matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); ArrayList<String> best = new ArrayList<>(); best.add(matches.get(0)); search.populateEditText(best); } else if (requestCode == MY_DATA_CHECK_CODE) { if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) { t1 = new TextToSpeech(getApplicationContext(), this); } } super.onActivityResult(requestCode, resultCode, data); } @Override public void startActivityForResult(Intent intent, int requestCode, Bundle options) { //TODO Hack to force the search box voice recognition language to en-US. The code of the search box should be modified in the future to remove this hack. forceSpeechRecognitionLanguage(intent, requestCode); super.startActivityForResult(intent, requestCode, options); } @Override public void startActivityForResult(Intent intent, int requestCode) { //TODO Hack to force the search box voice recognition language to en-US. The code of the search box should be modified in the future to remove this hack. forceSpeechRecognitionLanguage(intent, requestCode); super.startActivityForResult(intent, requestCode); } private void forceSpeechRecognitionLanguage(Intent intent, int requestCode) { if (requestCode == SearchBox.VOICE_RECOGNITION_CODE) { Log.d(TAG, "Recognition language: " + Locale.US.toString()); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.US.toString()); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (response != null) { outState.putParcelable(RESPONSE_STATE, response); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState.containsKey(RESPONSE_STATE)) { response = savedInstanceState.getParcelable(RESPONSE_STATE); bus.post(new ResponseChangedAction(response)); } } @Override public void onSearchOpened() { searchOpened = true; Log.d(TAG, "Search opened."); } @Override public void onSearchCleared() { } @Override public void onSearchClosed() { searchOpened = false; Log.d(TAG, "Search closed."); } @Override public void onSearchTermChanged() { } @Override public void onSearch(String s) { ArrayList<SearchResult> searchables = search.getSearchables(); List<SearchResult> toRemove = new LinkedList<>(); for (SearchResult result : searchables) { if (result.title.equals(s)) { toRemove.add(result); } } searchables.removeAll(toRemove); searchables.add(0, new SearchResult(s, historyDrawable)); SQLiteDatabase db = dbHelper.getWritableDatabase(); addResult(db, s); db.close(); bus.post(new RequestUpdateAction()); initiateSearch(s); } @Transactional protected void addResult(SQLiteDatabase db, String s) { SearchItem.insert(s, db); } private void initiateSearch(String term) { synchronized (lock) { if (executer != null) { executer.cancel(true); } response = null; executer = new YodaExecuter(bus, restClient); executer.execute(term); } } @Override public void onBackPressed() { if (searchOpened) { search.toggleSearch(); } else { super.onBackPressed(); } } @OptionsItem(R.id.menu_settings) public void onSettingsMenuItemClicked() { Intent intent = new Intent(this, SettingsActivity_.class); startActivity(intent); } @OptionsItem(R.id.menu_report_error) public void onReportErrorMenuItemClicked() { new ReportDialogFragment().show(getFragmentManager(), "dialog"); } @Override public void onInit(int status) { Log.d(TAG, "Text to speech init " + status); if (status == TextToSpeech.SUCCESS) { int res; if (t1.isLanguageAvailable(Locale.US) == TextToSpeech.LANG_COUNTRY_AVAILABLE) { res = t1.setLanguage(Locale.US); } else { res = t1.setLanguage(Locale.UK); } if (res == TextToSpeech.LANG_NOT_SUPPORTED || res == TextToSpeech.LANG_MISSING_DATA) { Log.d(TAG, "Language not supported"); } } else { Log.d(TAG, "Text to speech init failed. " + status); } } }