package com.integralblue.callerid; import java.text.MessageFormat; import java.util.HashMap; import java.util.Random; import roboguice.service.RoboService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.PixelFormat; import android.media.AudioManager; import android.os.IBinder; import android.speech.tts.TextToSpeech; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.Toast; import com.google.inject.Inject; import com.integralblue.callerid.CallerIDLookup.NoResultException; import com.integralblue.callerid.contacts.ContactsHelper; import com.integralblue.callerid.inject.TextToSpeechHelper; import com.integralblue.callerid.inject.VersionInformationHelper; public class CallerIDService extends RoboService { @Inject ContactsHelper contactsHelper; @Inject WindowManager windowManager; @Inject NotificationManager notificationManager; @Inject LayoutInflater layoutInflater; //@InjectView(R.layout.toast) ViewGroup toastLayout; @Inject CallerIDLookup callerIDLookup; @Inject VersionInformationHelper versionInformationHelper; @Inject SharedPreferences sharedPreferences; @Inject TextToSpeechHelper textToSpeechHelper; //@InjectResource(R.integer.default_popup_horizontal_gravity) int defaultPopupHorizontalGravity; //@InjectResource(R.integer.default_popup_vertical_gravity) int defaultPopupVerticalGravity; boolean defaultPopupMap; String previousPhoneState = TelephonyManager.EXTRA_STATE_IDLE; String previousPhoneNumber = null; String previousCallerID = null; boolean ttsEnabled; static final HashMap<String, String> ttsParametersMap; static { ttsParametersMap = new HashMap<String, String>(); ttsParametersMap.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_RING)); } class ToastLookupAsyncTask extends LookupAsyncTask { public ToastLookupAsyncTask(Context context, CharSequence phoneNumber) { super(context, phoneNumber,toastLayout,sharedPreferences.getBoolean("popup_map", defaultPopupMap)); } @Override protected void onSuccess(final CallerIDResult result) throws Exception { super.onSuccess(result); previousCallerID = result.getName(); if(isCancelled()) return; //don't do any UI/TTS things if the task was cancelled toastLayout.setVisibility(View.VISIBLE); if(versionInformationHelper.shouldPromptForNewVersion()){ Toast.makeText(CallerIDService.this, R.string.new_version_dialog_title, Toast.LENGTH_LONG).show(); } if(ttsEnabled){ textToSpeechHelper.speak(getString(R.string.incoming_call_tts, result.getName()), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); } } @Override protected void onPreExecute() throws Exception { super.onPreExecute(); toastLayout.setVisibility(View.VISIBLE); previousCallerID = offlineGeocoderResult; } @Override protected void onException(Exception e) throws RuntimeException { super.onException(e); previousCallerID = offlineGeocoderResult; if(isCancelled()) return; //don't do any UI/TTS things if the task was cancelled toastLayout.setVisibility(View.VISIBLE); if (e instanceof CallerIDLookup.NoResultException) { if(offlineGeocoderResult == null) textToSpeechHelper.speak(getString(R.string.incoming_call_tts_unknown), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); else textToSpeechHelper.speak(getString(R.string.incoming_call_tts, offlineGeocoderResult), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); } } @Override protected void onInterrupted(Exception e) { super.onInterrupted(e); toastLayout.setVisibility(View.GONE); previousCallerID = null; } } LookupAsyncTask currentLookupAsyncTask = null; // This is the old onStart method that will be called on the pre-2.0 // platform. On 2.0 or later we override onStartCommand() so this // method will not be called. @Override public void onStart(Intent intent, int startId) { handleCommand(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { handleCommand(intent, startId); return START_NOT_STICKY; } protected void handleCommand(Intent intent, int startId){ final String phoneState = intent .getStringExtra(TelephonyManager.EXTRA_STATE); final String phoneNumber = intent .getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); // since we're about to start a new lookup or the phone stopped ringing, // we want to cancel any lookups in progress if (currentLookupAsyncTask != null) currentLookupAsyncTask.cancel(true); if (TelephonyManager.EXTRA_STATE_RINGING.equals(phoneState)) { if(TextUtils.isEmpty(phoneNumber) || SpecialPhoneNumbers.UNKNOWN_NUMBER.equals(phoneNumber)){ toastLayout.setVisibility(View.GONE); textToSpeechHelper.speak(getString(R.string.incoming_call_tts_unknown), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); }else if(SpecialPhoneNumbers.PRIVATE_NUMBER.equals(phoneNumber)){ toastLayout.setVisibility(View.GONE); textToSpeechHelper.speak(getString(R.string.incoming_call_tts_private), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); }else if(SpecialPhoneNumbers.PAYPHONE_NUMBER.equals(phoneNumber)){ toastLayout.setVisibility(View.GONE); textToSpeechHelper.speak(getString(R.string.incoming_call_tts_payphone), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); }else{ try{ CallerIDResult result = contactsHelper.getContact(phoneNumber); toastLayout.setVisibility(View.GONE); //speak the contact's name even when we don't need to use the CallerID service to get information if(ttsEnabled && result.getName()!=null && result.getName()!=""){ textToSpeechHelper.speak(getString(R.string.incoming_call_tts, result.getName()), TextToSpeech.QUEUE_FLUSH, ttsParametersMap); } }catch(NoResultException e){ currentLookupAsyncTask = new ToastLookupAsyncTask(this, phoneNumber); currentLookupAsyncTask.execute(); } } } else { toastLayout.setVisibility(View.GONE); stopSelf(startId); } if (TelephonyManager.EXTRA_STATE_IDLE.equals(phoneState) && TelephonyManager.EXTRA_STATE_RINGING.equals(previousPhoneState) && previousPhoneNumber!=null && !contactsHelper.haveContactWithPhoneNumber(previousPhoneNumber)){ //add missed call notification final Notification notification = new Notification( android.R.drawable.sym_call_missed, previousCallerID==null?getString(R.string.missed_call_from_unknown):MessageFormat.format(getString(R.string.missed_call_from_known),previousCallerID), System.currentTimeMillis()); final Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); notificationIntent.putExtra("phoneNumber", previousPhoneNumber); final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(getApplicationContext(), getString(R.string.missed_call), previousCallerID==null?getString(R.string.perform_lookup_label):previousCallerID, contentIntent); notification.flags |= Notification.FLAG_AUTO_CANCEL; notificationManager.notify(new Random().nextInt(), notification); if(ttsEnabled){ textToSpeechHelper.stop(); } } previousPhoneNumber = phoneNumber; previousPhoneState = phoneState; } @Override public void onCreate() { super.onCreate(); toastLayout = (ViewGroup) layoutInflater.inflate(R.layout.toast, null); defaultPopupHorizontalGravity = getResources().getInteger(R.integer.default_popup_horizontal_gravity); defaultPopupVerticalGravity = getResources().getInteger(R.integer.default_popup_vertical_gravity); defaultPopupMap = Boolean.parseBoolean(getResources().getString(R.string.default_popup_map)); WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); //params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; params.gravity = 0; String popupVerticalGravity = sharedPreferences.getString("popup_vertical_gravity", null); if(popupVerticalGravity == null) params.gravity |= defaultPopupVerticalGravity; else params.gravity |= Integer.parseInt(popupVerticalGravity); String popupHorizontalGravity = sharedPreferences.getString("popup_horizontal_gravity", null); if(popupHorizontalGravity == null) params.gravity |= defaultPopupHorizontalGravity; else params.gravity |= Integer.parseInt(popupHorizontalGravity); ttsEnabled = sharedPreferences.getBoolean("tts_enabled", true); toastLayout.setVisibility(View.GONE); windowManager.addView(toastLayout, params); } @Override public IBinder onBind(Intent intent) { // Does not support binding return null; } @Override public void onDestroy() { windowManager.removeView(toastLayout); textToSpeechHelper.shutdown(); } }