package com.mmmoussa.iqra; import android.Manifest; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.AsyncTask; import android.speech.RecognitionListener; import android.speech.RecognizerIntent; import android.speech.SpeechRecognizer; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.SpannableString; import android.text.style.RelativeSizeSpan; import android.util.Log; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewConfiguration; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.analytics.HitBuilders; import com.google.android.gms.analytics.Tracker; import com.mmmoussa.iqra.netcomm.NetworkRequestCallback; import com.mmmoussa.iqra.netcomm.RequestDelegate; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; public class MainActivity extends AppCompatActivity implements RecognitionListener { private Context context; private final int MY_PERMISSIONS_REQUEST_INTERNET = 100; private final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 200; private boolean hasInternetPermission; private boolean hasRecordAudioPermission; public static final String TAG = MainActivity.class.getSimpleName(); private SpeechRecognizer mSpeechRecognizer = null; private Intent mSpeechRecognizerIntent; private boolean mIsListening; private ArrayList<Integer> voiceLevelChanges; private ImageButton recordButton; private TextView micText; private ImageView recordCircle; private TextView partialResult; private SharedPreferences prefs; private Tracker mTracker; private String screenName = "Home"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this; android.support.v7.app.ActionBar ab = getSupportActionBar(); if (ab != null) { ab.setElevation(0); ab.setDisplayShowTitleEnabled(false); } try { ViewConfiguration config = ViewConfiguration.get(this); Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); if (menuKeyField != null) { menuKeyField.setAccessible(true); menuKeyField.setBoolean(config, false); } } catch (Exception ex) { // Ignore } // Obtain the shared Tracker instance. AnalyticsApplication application = (AnalyticsApplication) getApplication(); mTracker = application.getDefaultTracker(); hasInternetPermission = false; hasRecordAudioPermission = false; isOnline(); doPermissionCheck(); doGoogleAppCheck(); prefs = this.getSharedPreferences("com.mmmoussa.iqra", MODE_PRIVATE); recordButton = (ImageButton) findViewById(R.id.recordButton); micText = (TextView) findViewById(R.id.micText); recordCircle = (ImageView) findViewById(R.id.recordCircle); partialResult = (TextView) findViewById(R.id.partialResult); voiceLevelChanges = new ArrayList<>(); setupSpeechInput(); } @Override protected void onResume() { super.onResume(); mTracker.setScreenName(screenName); mTracker.send(new HitBuilders.ScreenViewBuilder().build()); recordCircle.getLayoutParams().width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.getLayoutParams().height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.requestLayout(); } @Override protected void onDestroy() { super.onDestroy(); if (mSpeechRecognizer != null) { mSpeechRecognizer.destroy(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_about: Intent aboutIntent = new Intent(context, AboutActivity.class); startActivity(aboutIntent); break; case R.id.action_contact: Intent contactIntent = new Intent(context, ContactActivity.class); startActivity(contactIntent); break; case R.id.action_share: share(); break; case R.id.action_settings: Intent settingsIntent = new Intent(context, SettingsActivity.class); startActivity(settingsIntent); break; default: break; } return super.onOptionsItemSelected(item); } private void share() { Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); sharingIntent.setType("text/plain"); sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.share_subject)); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, getResources().getString(R.string.share_message)); startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.share_with))); } private void isOnline() { AsyncTask.execute(new Runnable() { @Override public void run() { boolean online = false; Runtime runtime = Runtime.getRuntime(); try { Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8"); int exitValue = ipProcess.waitFor(); online = (exitValue == 0); } catch (IOException | InterruptedException e) { e.printStackTrace(); } if (!online) { MainActivity.this.runOnUiThread(new Runnable() { public void run() { Toast.makeText(context, getResources().getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show(); } }); } } }); } private void doGoogleAppCheck() { try { ApplicationInfo ai = getPackageManager().getApplicationInfo("com.google.android.googlequicksearchbox", 0); if (!ai.enabled) { Intent googleAppIntent = new Intent(context, RequirementsDialogActivity.class); googleAppIntent.putExtra("requirementType", "googleDisabled"); startActivity(googleAppIntent); } } catch (PackageManager.NameNotFoundException e) { Intent googleAppIntent = new Intent(context, RequirementsDialogActivity.class); googleAppIntent.putExtra("requirementType", "googleMissing"); startActivity(googleAppIntent); } } private void doPermissionCheck() { if (ContextCompat.checkSelfPermission(context, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.INTERNET}, MY_PERMISSIONS_REQUEST_INTERNET); } else { hasInternetPermission = true; } if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, MY_PERMISSIONS_REQUEST_RECORD_AUDIO); } else { hasRecordAudioPermission = true; } } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_INTERNET: // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { hasInternetPermission = true; } else { Intent permissionIntent = new Intent(context, RequirementsDialogActivity.class); permissionIntent.putExtra("requirementType", "permission"); permissionIntent.putExtra("permissionName", getResources().getString(R.string.internet)); startActivity(permissionIntent); } break; case MY_PERMISSIONS_REQUEST_RECORD_AUDIO: // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { hasRecordAudioPermission = true; } else { Intent permissionIntent = new Intent(context, RequirementsDialogActivity.class); permissionIntent.putExtra("requirementType", "permission"); permissionIntent.putExtra("permissionName", getResources().getString(R.string.record_audio)); startActivity(permissionIntent); } break; } } public void listen(View view) { if (hasInternetPermission && hasRecordAudioPermission) { if (!mIsListening) { Log.d(TAG, "Started listening"); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); voiceLevelChanges.clear(); voiceLevelChanges.addAll(Arrays.asList(90, 90, 90, 90, 90)); recordCircle.setImageResource(R.drawable.record_circle_active); } else { mSpeechRecognizer.stopListening(); } mIsListening = !mIsListening; } else { doPermissionCheck(); } } protected void setupSpeechInput() { mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this); mSpeechRecognizer.setRecognitionListener(this); mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); if (!mSpeechRecognizerIntent.hasExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE)) { mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName()); } mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ar-AE"); mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true); mIsListening = false; } private void callApi(String arabicText) { lockScreenOrientation(); SpannableString ss1 = new SpannableString(getResources().getString(R.string.getting_match)); ss1.setSpan(new RelativeSizeSpan(1.7f), 0, ss1.length(), 0); final ProgressDialog progress = new ProgressDialog(this); progress.setMessage(ss1); progress.setCancelable(false); progress.show(); RequestDelegate requestDelegate = RequestDelegate.getInstance(context); requestDelegate.performSearchQuery(arabicText, prefs.getString("translation", "en-hilali"), new NetworkRequestCallback() { @Override public void onSuccess(JSONObject response) { progress.dismiss(); Log.v(TAG, response.toString()); parseSearchQueryResponse(response); } @Override public void onFailure(Throwable error) { progress.dismiss(); onSearchQueryError(error); } }); } private void parseSearchQueryResponse(JSONObject response) { try { JSONObject result = response.getJSONObject("result"); JSONArray matches = result.getJSONArray("matches"); int numOfMatches = matches.length(); if (numOfMatches == 0) { Toast.makeText(getApplicationContext(), getResources().getString(R.string.no_matches), Toast.LENGTH_SHORT).show(); } else { Intent intent = new Intent(getApplicationContext(), SearchResultsActivity.class); if (numOfMatches > 150) { Toast.makeText(getApplicationContext(), getResources().getString(R.string.too_many_results), Toast.LENGTH_LONG).show(); JSONArray shortenedMatches = new JSONArray(); for (int i = 0; i < 150; i++) { shortenedMatches.put(matches.get(i)); } result.put("matches", shortenedMatches); } Log.d(TAG, "Number of matches: " + numOfMatches); intent.putExtra("response", result.toString()); intent.putExtra("numOfMatches", numOfMatches); startActivity(intent); } } catch (JSONException je) { Log.e("API result problem: ", je.getMessage()); } } private void onSearchQueryError(Throwable error) { String errorMessage = error.getMessage(); if (errorMessage == null) { Log.e("API result problem: ", "Socket Timeout"); Toast.makeText(getApplicationContext(), getResources().getString(R.string.server_connection_lost), Toast.LENGTH_SHORT).show(); } else { Log.e("API result problem: ", errorMessage); Toast.makeText(getApplicationContext(), getResources().getString(R.string.something_went_wrong), Toast.LENGTH_SHORT).show(); } } private void lockScreenOrientation() { int currentOrientation = getResources().getConfiguration().orientation; if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } } private void unlockScreenOrientation() { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); } /** * Methods for RecognitionListener */ @Override public void onBeginningOfSpeech() { Log.d(TAG, "onBeginingOfSpeech"); micText.setText(getString(R.string.now_recording)); recordCircle.getLayoutParams().width = 90; recordCircle.getLayoutParams().height = 90; recordCircle.requestLayout(); recordCircle.setImageResource(R.drawable.record_circle_active); } @Override public void onBufferReceived(byte[] buffer) { } @Override public void onEndOfSpeech() { Log.d(TAG, "onEndOfSpeech"); micText.setText(getString(R.string.done_recording)); recordCircle.getLayoutParams().width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.getLayoutParams().height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.requestLayout(); recordCircle.setImageResource(R.drawable.record_circle_inactive); } @Override public void onError(int error) { String mError = ""; switch (error) { case SpeechRecognizer.ERROR_NETWORK_TIMEOUT: mError = getResources().getString(R.string.error_network_timeout); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); break; case SpeechRecognizer.ERROR_NETWORK: mError = getResources().getString(R.string.error_network); mSpeechRecognizer.cancel(); mIsListening = false; recordCircle.setImageResource(R.drawable.record_circle_inactive); break; case SpeechRecognizer.ERROR_AUDIO: mError = getResources().getString(R.string.error_audio); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); break; case SpeechRecognizer.ERROR_SERVER: mError = getResources().getString(R.string.error_server); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); break; case SpeechRecognizer.ERROR_CLIENT: mError = getResources().getString(R.string.error_client); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); break; case SpeechRecognizer.ERROR_SPEECH_TIMEOUT: mError = getResources().getString(R.string.error_speech_timeout); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); break; case SpeechRecognizer.ERROR_NO_MATCH: mError = getResources().getString(R.string.error_no_match); mSpeechRecognizer.startListening(mSpeechRecognizerIntent); break; case SpeechRecognizer.ERROR_RECOGNIZER_BUSY: mError = getResources().getString(R.string.error_recognizer_busy); mSpeechRecognizer.cancel(); mIsListening = false; recordCircle.setImageResource(R.drawable.record_circle_inactive); break; case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS: mError = getResources().getString(R.string.error_insufficient_permissions); mSpeechRecognizer.cancel(); mIsListening = false; recordCircle.setImageResource(R.drawable.record_circle_inactive); break; } Log.i(TAG, "Error: " + error + " - " + mError); micText.setText(mError); recordCircle.getLayoutParams().width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.getLayoutParams().height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.requestLayout(); partialResult.setText(""); } @Override public void onEvent(int eventType, Bundle params) { } @Override public void onPartialResults(Bundle partialResults) { Log.d(TAG, "onPartialResults"); ArrayList<String> results = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); if (results != null) { partialResult.setText(results.get(0)); } } @Override public void onReadyForSpeech(Bundle params) { Log.d(TAG, "onReadyForSpeech"); //$NON-NLS-1$ micText.setText(getString(R.string.begin_recording)); recordCircle.getLayoutParams().width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.getLayoutParams().height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.requestLayout(); } @Override public void onResults(Bundle results) { mIsListening = false; micText.setText(getString(R.string.tap_on_mic)); recordCircle.getLayoutParams().width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.getLayoutParams().height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.requestLayout(); recordCircle.setImageResource(R.drawable.record_circle_inactive); partialResult.setText(""); // Log.d(TAG, "onResults"); //$NON-NLS-1$ ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); // matches are the return values of speech recognition engine if (matches != null) { // Log.d(TAG, matches.toString()); //$NON-NLS-1$ callApi(matches.get(0)); } else { Toast.makeText(getApplicationContext(), getResources().getString(R.string.cannot_understand), Toast.LENGTH_SHORT).show(); } } @Override public void onRmsChanged(float rmsdB) { if (rmsdB < 0) { rmsdB = 0; } int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (rmsdB * 8) + 80, getResources().getDisplayMetrics()); voiceLevelChanges.remove(0); voiceLevelChanges.add(size); int adjustedSize = 0; for (int i = 0; i < voiceLevelChanges.size(); i++) { adjustedSize += voiceLevelChanges.get(i); } adjustedSize = adjustedSize / voiceLevelChanges.size(); if (!mIsListening) { recordCircle.getLayoutParams().width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.getLayoutParams().height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics()); recordCircle.setImageResource(R.drawable.record_circle_inactive); } else { recordCircle.getLayoutParams().width = adjustedSize; recordCircle.getLayoutParams().height = adjustedSize; } recordCircle.requestLayout(); } }