/* * Copyright (C) 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.googlecode.android_scripting.facade; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.net.URLEncoder; import java.util.List; import java.util.concurrent.Callable; import android.app.Service; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.Contacts.PhonesColumns; import android.telephony.CellLocation; import android.telephony.NeighboringCellInfo; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import com.googlecode.android_scripting.MainThread; import com.googlecode.android_scripting.jsonrpc.RpcReceiver; import com.googlecode.android_scripting.rpc.Rpc; import com.googlecode.android_scripting.rpc.RpcParameter; import com.googlecode.android_scripting.rpc.RpcStartEvent; import com.googlecode.android_scripting.rpc.RpcStopEvent; /** * Exposes TelephonyManager functionality. * * @author Damon Kohler (damonkohler@gmail.com) * @author Felix Arends (felix.arends@gmail.com) */ @SuppressWarnings("deprecation") public class PhoneFacade extends RpcReceiver { private final AndroidFacade mAndroidFacade; private final EventFacade mEventFacade; private final TelephonyManager mTelephonyManager; private final Bundle mPhoneState; private final Service mService; private PhoneStateListener mPhoneStateListener; public PhoneFacade(FacadeManager manager) { super(manager); mService = manager.getService(); mTelephonyManager = (TelephonyManager) mService .getSystemService(Context.TELEPHONY_SERVICE); mAndroidFacade = manager.getReceiver(AndroidFacade.class); mEventFacade = manager.getReceiver(EventFacade.class); mPhoneState = new Bundle(); mPhoneStateListener = MainThread.run(mService, new Callable<PhoneStateListener>() { @Override public PhoneStateListener call() throws Exception { return new PhoneStateListener() { @Override public void onCallStateChanged(int state, String incomingNumber) { mPhoneState.putString("incomingNumber", incomingNumber); switch (state) { case TelephonyManager.CALL_STATE_IDLE: mPhoneState.putString("state", "idle"); break; case TelephonyManager.CALL_STATE_OFFHOOK: mPhoneState.putString("state", "offhook"); break; case TelephonyManager.CALL_STATE_RINGING: mPhoneState.putString("state", "ringing"); break; } mEventFacade.postEvent("phone", mPhoneState.clone()); } }; } }); } @Override public void shutdown() { stopTrackingPhoneState(); } @Rpc(description = "Starts tracking phone state.") @RpcStartEvent("phone") public void startTrackingPhoneState() { mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); } @Rpc(description = "Returns the current phone state and incoming number.", returns = "A Map of \"state\" and \"incomingNumber\"") public Bundle readPhoneState() { return mPhoneState; } @Rpc(description = "Stops tracking phone state.") @RpcStopEvent("phone") public void stopTrackingPhoneState() { mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); } @Rpc(description = "Calls a contact/phone number by URI.") public void phoneCall(@RpcParameter(name = "uri") final String uriString) throws Exception { Uri uri = Uri.parse(uriString); if (uri.getScheme().equals("content")) { String phoneNumberColumn = PhonesColumns.NUMBER; String selectWhere = null; if ((FacadeManager.class.cast(mManager)).getSdkLevel() >= 5) { Class<?> contactsContract_Data_class = Class .forName("android.provider.ContactsContract$Data"); Field RAW_CONTACT_ID_field = contactsContract_Data_class .getField("RAW_CONTACT_ID"); selectWhere = RAW_CONTACT_ID_field.get(null).toString() + "=" + uri.getLastPathSegment(); Field CONTENT_URI_field = contactsContract_Data_class .getField("CONTENT_URI"); uri = Uri.parse(CONTENT_URI_field.get(null).toString()); Class<?> ContactsContract_CommonDataKinds_Phone_class = Class .forName("android.provider.ContactsContract$CommonDataKinds$Phone"); Field NUMBER_field = ContactsContract_CommonDataKinds_Phone_class .getField("NUMBER"); phoneNumberColumn = NUMBER_field.get(null).toString(); } ContentResolver resolver = mService.getContentResolver(); Cursor c = resolver.query(uri, new String[] { phoneNumberColumn }, selectWhere, null, null); String number = ""; if (c.moveToFirst()) { number = c .getString(c.getColumnIndexOrThrow(phoneNumberColumn)); } c.close(); phoneCallNumber(number); } else { mAndroidFacade.startActivity(Intent.ACTION_CALL, uriString, null, null, null, null, null); } } @Rpc(description = "Calls a phone number.") public void phoneCallNumber( @RpcParameter(name = "phone number") final String number) throws Exception { phoneCall("tel:" + URLEncoder.encode(number, "ASCII")); } @Rpc(description = "Dials a contact/phone number by URI.") public void phoneDial(@RpcParameter(name = "uri") final String uri) throws Exception { mAndroidFacade.startActivity(Intent.ACTION_DIAL, uri, null, null, null, null, null); } @Rpc(description = "Dials a phone number.") public void phoneDialNumber( @RpcParameter(name = "phone number") final String number) throws Exception, UnsupportedEncodingException { phoneDial("tel:" + URLEncoder.encode(number, "ASCII")); } @Rpc(description = "Returns the current cell location.") public CellLocation getCellLocation() { return mTelephonyManager.getCellLocation(); } @Rpc(description = "Returns the numeric name (MCC+MNC) of current registered operator.") public String getNetworkOperator() { return mTelephonyManager.getNetworkOperator(); } @Rpc(description = "Returns the alphabetic name of current registered operator.") public String getNetworkOperatorName() { return mTelephonyManager.getNetworkOperatorName(); } @Rpc(description = "Returns a the radio technology (network type) currently in use on the device.") public String getNetworkType() { // TODO(damonkohler): API level 5 has many more types. switch (mTelephonyManager.getNetworkType()) { case TelephonyManager.NETWORK_TYPE_EDGE: return "edge"; case TelephonyManager.NETWORK_TYPE_GPRS: return "gprs"; case TelephonyManager.NETWORK_TYPE_UMTS: return "umts"; case TelephonyManager.NETWORK_TYPE_UNKNOWN: return "unknown"; default: return null; } } @Rpc(description = "Returns the device phone type.") public String getPhoneType() { // TODO(damonkohler): API level 4 includes CDMA. switch (mTelephonyManager.getPhoneType()) { case TelephonyManager.PHONE_TYPE_GSM: return "gsm"; case TelephonyManager.PHONE_TYPE_NONE: return "none"; default: return null; } } @Rpc(description = "Returns the ISO country code equivalent for the SIM provider's country code.") public String getSimCountryIso() { return mTelephonyManager.getSimCountryIso(); } @Rpc(description = "Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits.") public String getSimOperator() { return mTelephonyManager.getSimOperator(); } @Rpc(description = "Returns the Service Provider Name (SPN).") public String getSimOperatorName() { return mTelephonyManager.getSimOperatorName(); } @Rpc(description = "Returns the serial number of the SIM, if applicable. Return null if it is unavailable.") public String getSimSerialNumber() { return mTelephonyManager.getSimSerialNumber(); } @Rpc(description = "Returns the state of the device SIM card.") public String getSimState() { switch (mTelephonyManager.getSimState()) { case TelephonyManager.SIM_STATE_UNKNOWN: return "uknown"; case TelephonyManager.SIM_STATE_ABSENT: return "absent"; case TelephonyManager.SIM_STATE_PIN_REQUIRED: return "pin_required"; case TelephonyManager.SIM_STATE_PUK_REQUIRED: return "puk_required"; case TelephonyManager.SIM_STATE_NETWORK_LOCKED: return "network_locked"; case TelephonyManager.SIM_STATE_READY: return "ready"; default: return null; } } @Rpc(description = "Returns the unique subscriber ID, for example, the IMSI for a GSM phone. Return null if it is unavailable.") public String getSubscriberId() { return mTelephonyManager.getSubscriberId(); } @Rpc(description = "Retrieves the alphabetic identifier associated with the voice mail number.") public String getVoiceMailAlphaTag() { return mTelephonyManager.getVoiceMailAlphaTag(); } @Rpc(description = "Returns the voice mail number. Return null if it is unavailable.") public String getVoiceMailNumber() { return mTelephonyManager.getVoiceMailNumber(); } @Rpc(description = "Returns true if the device is considered roaming on the current network, for GSM purposes.") public Boolean checkNetworkRoaming() { return mTelephonyManager.isNetworkRoaming(); } @Rpc(description = "Returns the unique device ID, for example, the IMEI for GSM and the MEID for CDMA phones. Return null if device ID is not available.") public String getDeviceId() { return mTelephonyManager.getDeviceId(); } @Rpc(description = "Returns the software version number for the device, for example, the IMEI/SV for GSM phones. Return null if the software version is not available.") public String getDeviceSoftwareVersion() { return mTelephonyManager.getDeviceSoftwareVersion(); } @Rpc(description = "Returns the phone number string for line 1, for example, the MSISDN for a GSM phone. Return null if it is unavailable.") public String getLine1Number() { return mTelephonyManager.getLine1Number(); } @Rpc(description = "Returns the neighboring cell information of the device.") public List<NeighboringCellInfo> getNeighboringCellInfo() { return mTelephonyManager.getNeighboringCellInfo(); } }